The page you are looking for does not exist anymore or never has existed. If you have found this page through a link, you should tell the link author to update it.
Maybe one of these can help you to find the desired content:
When you use PouchDB RxStorage, there are many adapters that define where the data has to be stored.
-Depending on which environment you work in, you can choose between different adapters. For example, in the browser you want to store the data inside of IndexedDB but on NodeJS you want to store the data on the filesystem.
-
This page is an overview over the different adapters with recommendations on what to use where.
-
-
warning
The PouchDB RxStorage is removed from RxDB and can no longer be used in new projects. You should switch to a different RxStorage.
-
-
Please always ensure that your pouchdb adapter-version is the same as pouchdb-core in the rxdb package.json. Otherwise, you might have strange problems.
In any environment, you can use the memory-adapter. It stores the data in the javascript runtime memory. This means it is not persistent and the data is lost when the process terminates.
-
Use this adapter when:
-
-
You want to have really good performance
-
You do not want persistent state, for example in your test suite
A reimplementation of the indexeddb adapter which uses native secondary indexes. Should have a much better performance but can behave different on some edge cases.
-
note
Multiple users have reported problems with this adapter. It is not recommended to use this adapter.
This adapter stores the data inside of websql. It has a different performance behavior. Websql is deprecated. You should not use the websql adapter unless you have a really good reason.
This adapter uses a LevelDB C++ binding to store that data on the filesystem. It has the best performance compared to other filesystem adapters. This adapter can not be used when multiple nodejs-processes access the same filesystem folders for storage.
-
// npm install leveldown --save
-// npm install pouchdb-adapter-leveldb --save
-addPouchPlugin(require('pouchdb-adapter-leveldb')); // leveldown adapters need the leveldb plugin to work
-const leveldown = require('leveldown');
-
-const database = await createRxDatabase({
- name: 'mydatabase',
- storage: getRxStoragePouch(leveldown) // the full leveldown-module
-});
-
-// or use a specific folder to store the data
-const database = await createRxDatabase({
- name: '/root/user/project/mydatabase',
- storage: getRxStoragePouch(leveldown) // the full leveldown-module
-});
This adapter uses the node-websql-shim to store data on the filesystem. Its advantages are that it does not need a leveldb build and it can be used when multiple nodejs-processes use the same database-files.
-
// npm install pouchdb-adapter-node-websql --save
-addPouchPlugin(require('pouchdb-adapter-node-websql'));
-
-const database = await createRxDatabase({
- name: 'mydatabase',
- storage: getRxStoragePouch('websql') // the name of your adapter
-});
-
-// or use a specific folder to store the data
-const database = await createRxDatabase({
- name: '/root/user/project/mydatabase',
- storage: getRxStoragePouch('websql') // the name of your adapter
-});
Uses cordova's global cordova.sqlitePlugin. It can be used with cordova and capacitor.
-
// npm install pouchdb-adapter-cordova-sqlite --save
-addPouchPlugin(require('pouchdb-adapter-cordova-sqlite'));
-
-/**
- * In capacitor/cordova you have to wait until all plugins are loaded and 'window.sqlitePlugin'
- * can be accessed.
- * This function waits until document deviceready is called which ensures that everything is loaded.
- * @link https://cordova.apache.org/docs/de/latest/cordova/events/events.deviceready.html
- */
-export function awaitCapacitorDeviceReady(): Promise<void> {
- return new Promise(res => {
- document.addEventListener('deviceready', () => {
- res();
- });
- });
-}
-
-async function getDatabase(){
-
- // first wait until the deviceready event is fired
- await awaitCapacitorDeviceReady();
-
- const database = await createRxDatabase({
- name: 'mydatabase',
- storage: getRxStoragePouch(
- 'cordova-sqlite',
- // pouch settings are passed as second parameter
- {
- // for ios devices, the cordova-sqlite adapter needs to know where to save the data.
- iosDatabaseLocation: 'Library'
- }
- )
- });
-}
Alternatives for realtime offline-first JavaScript applications
-
To give you an augmented view over the topic of client side JavaScript databases, this page contains all known alternatives to RxDB. Remember that you are reading this inside of the RxDB documentation, so everything is opinionated.
-If you disagree with anything or think that something is missing, make a pull request to this file on the RxDB github repository.
-
note
RxDB has these main benefits:
-
RxDB is a battle proven tool widely used by companies in real projects in production.
-
RxDB is not VC funded and therefore does not require you to use a specific cloud service to rip you off. RxDB can be used with your own backend or no backend at all.
-
RxDB has a working business model of selling premium plugins which ensures that RxDB will be maintained and improved continuously while many alternatives are dead already or seem to die soon.
-
RxDB has years (since 2016) of performance optimization, bug fixing and feature adding. It is just working as is and there are close to zero open issues.
RxDB is an observable, replicating, local first, JavaScript database. So it makes only sense to list similar projects as alternatives, not just any database or JavaScript store library. However, I will list up some projects that RxDB is often compared with, even if it only makes sense for some use cases.
-Here are the alternatives to RxDB:
Firebase is a platform developed by Google for creating mobile and web applications. Firebase has many features and products, two of which are client side databases. The Realtime Database and the Cloud Firestore.
The firebase realtime database was the first database in firestore. It has to be mentioned that in this context, "realtime" means "realtime replication", not "realtime computing". The firebase realtime database stores data as a big unstructured JSON tree that is replicated between clients and the backend.
The firestore is the successor to the realtime database. The big difference is that it behaves more like a 'normal' database that stores data as documents inside of collections. The conflict resolution strategy of firestore is always last-write-wins which might or might not be suitable for your use case.
-
The biggest difference to RxDB is that firebase products are only able to be used on top of the Firebase cloud hosted backend, which creates a vendor lock-in. RxDB can replicate with any self hosted CouchDB server or custom GraphQL endpoints. You can even replicate Firestore to RxDB with the Firestore Replication Plugin.
Meteor (since 2012) is one of the oldest technologies for JavaScript realtime applications. Meteor is not a library but a whole framework with its own package manager, database management and replication.
-Because of how it works, it has proven to be hard to integrate it with other modern JavaScript frameworks like angular, vue.js or svelte.
-
Meteor uses MongoDB in the backend and can replicate with a Minimongo database in the frontend.
-While testing, it has proven to be impossible to make a meteor app offline first capable. There are some projects that might do this, but all are unmaintained.
Forked in Jan 2014 from meteorJSs' minimongo package, Minimongo is a client-side, in-memory, JavaScript version of MongoDB with backend replication over HTTP. Similar to MongoDB, it stores data in documents inside of collections and also has the same query syntax. Minimongo has different storage adapters for IndexedDB, WebSQL, LocalStorage and SQLite.
-Compared to RxDB, Minimongo has no concept of revisions or conflict handling, which might lead to undefined behavior when used with replication or in multiple browser tabs. Minimongo has no observable queries or changestream.
WatermelonDB is a reactive & asynchronous JavaScript database. While originally made for React and React Native, it can also be used with other JavaScript frameworks. The main goal of WatermelonDB is performance within an application with lots of data.
-In React Native, WatermelonDB uses the provided SQLite database. Also there is an Expo plugin for WatermelonDB. In a browser, WatermelonDB uses the LokiJS in-memory database to store and query data. WatermelonDB is one of the rare projects that support both Flow and Typescript at the same time.
AWS Amplify is a collection of tools and libraries to develop web- and mobile frontend applications. Similar to firebase, it provides everything needed like authentication, analytics, a REST API, storage and so on. Everything hosted in the AWS Cloud, even when they state that "AWS Amplify is designed to be open and pluggable for any custom backend or service". For realtime replication, AWS Amplify can connect to an AWS App-Sync GraphQL endpoint.
Since December 2019 the Amplify library includes the AWS Datastore which is a document-based, client side database that is able to replicate data via AWS AppSync in the background.
-The main difference to other projects is the complex project configuration via the amplify cli and the bit confusing query syntax that works over functions. Complex Queries with multiple OR/AND statements are not possible which might change in the future.
-Local development is hard because the AWS AppSync mock does not support realtime replication. It also is not really offline-first because a user login is always required.
-
// An AWS datastore OR query
-const posts = await DataStore.query(Post, c => c.or(
- c => c.rating("gt", 4).status("eq", PostStatus.PUBLISHED)
-));
-
-// An AWS datastore SORT query
-const posts = await DataStore.query(Post, Predicates.ALL, {
- sort: s => s.rating(SortDirection.ASCENDING).title(SortDirection.DESCENDING)
-});
-
The biggest difference to RxDB is that you have to use the AWS cloud backends. This might not be a problem if your data is at AWS anyway.
RethinkDB is a backend database that pushed dynamic JSON data to the client in realtime. It was founded in 2009 and the company shut down in 2016.
-Rethink db is not a client side database, it streams data from the backend to the client which of course does not work while offline.
Horizon is the client side library for RethinkDB which provides useful functions like authentication, permission management and subscription to a RethinkDB backend. Offline support never made it to horizon.
Supabase labels itself as "an open source Firebase alternative". It is a collection of open source tools that together mimic many Firebase features, most of them by providing a wrapper around a PostgreSQL database. While it has realtime queries that run over the wire, like with RethinkDB, Supabase has no client-side storage or replication feature and therefore is not offline first.
Apache CouchDB is a server-side, document-oriented database that is mostly known for its multi-master replication feature. Instead of having a master-slave replication, with CouchDB you can run replication in any constellation without having a master server as bottleneck where the server even can go off- and online at any time. This comes with the drawback of having a slow replication with much network overhead.
-CouchDB has a changestream and a query syntax similar to MongoDB.
PouchDB is a JavaScript database that is compatible with most of the CouchDB API. It has an adapter system that allows you to switch out the underlying storage layer. There are many adapters like for IndexedDB, SQLite, the Filesystem and so on. The main benefit is to be able to replicate data with any CouchDB compatible endpoint.
-Because of the CouchDB compatibility, PouchDB has to do a lot of overhead in handling the revision tree of document, which is why it can show bad performance for bigger datasets.
-RxDB was originally build around PouchDB until the storage layer was abstracted out in version 10.0.0 so it now allows to use different RxStorage implementations. PouchDB has some performance issues because of how it has to store the document revision tree to stay compatible with the CouchDB API.
Couchbase (originally known as Membase) is another NoSQL document database made for realtime applications.
-It uses the N1QL query language which is more SQL like compared to other NoSQL query languages. In theory you can achieve replication of a Couchbase with a PouchDB database, but this has shown to be not that easy.
Cloudant is a cloud-based service that is based on CouchDB and has mostly the same features.
-It was originally designed for cloud computing where data can automatically be distributed between servers. But it can also be used to replicate with frontend PouchDB instances to create scalable web applications.
-It was bought by IBM in 2014 and since 2018 the Cloudant Shared Plan is retired and migrated to IBM Cloud.
Hoodie is a backend solution that enables offline-first JavaScript frontend development without having to write backend code. Its main goal is to abstract away configuration into simple calls to the Hoodie API.
-It uses CouchDB in the backend and PouchDB in the frontend to enable offline-first capabilities.
-The last commit for hoodie was one year ago and the website (hood.ie) is offline which indicates it is not an active project anymore.
LokiJS is a JavaScript embeddable, in-memory database. And because everything is handled in-memory, LokiJS has awesome performance when mutating or querying data. You can still persist to a permanent storage (IndexedDB, Filesystem etc.) with one of the provided storage adapters. The persistence happens after a timeout is reached after a write, or before the JavaScript process exits. This also means you could loose data when the JavaScript process exits ungracefully like when the power of the device is shut down or the browser crashes.
-While the project is not that active anymore, it is more finished than unmaintained.
-
In the past, RxDB supported using LokiJS as RxStorage but because the LokiJS is not maintained anymore and had too many issues, this storage option was removed in RxDB version 16.
GUN is a JavaScript graph database. While having many features, the decentralized replication is the main unique selling point. You can replicate data Peer-to-Peer without any centralized backend server. GUN has several other features that are useful on top of that, like encryption and authentication.
-
While testing it was really hard to get basic things running. GUN is open source, but because of how the source code is written, it is very difficult to understand what is going wrong.
sql.js is a javascript library to run SQLite on the web. It uses a virtual database file stored in memory and does not have any persistence. All data is lost once the JavaScript process exits. sql.js is created by compiling SQLite to WebAssembly so it has about the same features as SQLite. For older browsers there is a JavaScript fallback.
Absurd-sql is a project that implements an IndexedDB-based persistence for sql.js. Instead of directly writing data into the IndexedDB, it treats IndexedDB like a disk and stores data in blocks there which shows to have a much better performance, mostly because of how performance expensive IndexedDB transactions are.
NeDB was a embedded persistent or in-memory database for Node.js, nw.js, Electron and browsers.
-It is document-oriented and had the same query syntax as MongoDB.
-Like LokiJS it has persistence adapters for IndexedDB etc. to persist the database state on the disc.
-The last commit to NeDB was in 2016.
Dexie.js is a minimalistic wrapper for IndexedDB. While providing a better API than plain IndexedDB, Dexie also improves performance by batching transactions and other optimizations. It also adds additional non-IndexedDB features like observable queries or multi tab support or react hooks.
-Compared to RxDB, Dexie.js does not support complex (MongoDB-like) queries and requires a lot of fiddling when a document range of a specific index must be fetched.
-Dexie.js is used by Whatsapp Web, Microsoft To Do and Github Desktop.
-
RxDB supports using Dexie.js as Database storage which enhances IndexedDB via dexie with RxDB features like MongoDB-like queries etc.
LowDB is a small, local JSON database powered by the Lodash library. It is designed to be simple, easy to use, and straightforward. LowDB allows you to perform native JavaScript queries and persist data in a flat JSON file. Written in TypeScript, it's particularly well-suited for small projects, prototyping, or when you need a lightweight, file-based database.
-
As an alternative to LowDB, RxDB offers real-time reactivity, allowing developers to subscribe to database changes, a feature not natively available in LowDB. Additionally, RxDB provides robust query capabilities, including the ability to subscribe to query results for automatic UI updates. These features make RxDB a strong alternative to LowDB for more complex and dynamic applications.
localForage is a popular JavaScript library for offline storage that provides a simple, promise-based API. It abstracts over different storage mechanisms such as IndexedDB, WebSQL, or localStorage, making it easier to write code once and have it work seamlessly across various browsers. While localForage is great for storing data locally in a key-value manner, it doesn't provide the real-time reactive queries, conflict handling, or revision-based replication that RxDB does. This makes localForage a useful choice for straightforward caching or persistent storage needs, but not ideal for advanced offline-first scenarios requiring multi-user collaboration or complex querying.
Originally Realm was a mobile database for Android and iOS. Later they added support for other languages and runtimes, also for JavaScript.
-It was meant as replacement for SQLite but is more like an object store than a full SQL database.
-In 2019 MongoDB bought Realm and changed the projects focus.
-Now Realm is made for replication with the MongoDB Realm Sync based on the MongoDB Atlas Cloud platform. This tight coupling to the MongoDB cloud service is a big downside for most use cases.
The Apollo GraphQL platform is made to transfer data between a server to UI applications over GraphQL endpoints. It contains several tools like GraphQL clients in different languages or libraries to create GraphQL endpoints.
-
While it is has different caching features for offline usage, compared to RxDB it is not fully offline first because caching alone does not mean your application is fully usable when the user is offline.
Replicache is a client-side sync framework for building realtime, collaborative, local-first web apps. It claims to work with most backend stacks. In contrast to other local first tools, replicache does not work like a local database. Instead it runs on so called mutators that unify behavior on the client and server side. So instead of implementing and calling REST routes on both sides of your stack, you will implement mutators that define a specific delta behavior based on the input data. To observe data in replicache, there are subscriptions that notify your frontend application about changes to the state.
-Replicache can be used in most frontend technologies like browsers, React/Remix, NextJS/Vercel and React Native. While Replicache can be installed and used from npm, the Replicache source code is not open source and the Replicache github repo does not allow you to inspect or debug it. Still you can use replicache for in non-commercial projects, or for companies with < $200k revenue (ARR) and < $500k in funding. (2024: Replicache will be free and Rocicorp are working on a new Zerosync product to succeed Replicache and Reflect.)
InstantDB is designed for real-time data synchronization with built-in offline support, allowing changes to be queued locally and synced when the user reconnects. While it offers seamless optimistic updates and rollback capabilities, its offline-first design is not as mature or comprehensive as RxDB's - the offline data is more of a cache, not a full-database sync. The query language used is Datalog, and the backend sync service is written in Clojure. InstantDB is focused more on simplicity and real-time collaboration, with fewer customization options for storage or conflict resolution compared to RxDB, which supports various storage adapters and advanced conflict handling via CRDTs.
Yjs is a CRDT-based (Conflict-free Replicated Data Type) library focused on enabling real-time collaboration - particularly for text editing, although it can handle other data types as well. While it provides powerful conflict resolution and peer-to-peer synchronization out of the box, Yjs itself is not a full-fledged database. Instead, you typically combine Yjs with other storage or networking layers to achieve a local-first architecture. This flexibility allows for sophisticated real-time features, but also means you must handle indexing, queries, and persistence on your own if you need them. Compared to RxDB, Yjs does not offer built-in replication adapters or a query system, so developers who require a more complete solution for conflict resolution, data persistence, and offline-first capabilities may find RxDB more convenient.
2024: ElectricSQL is being rewritten in a new Electric-Next branch, which focuses on partial syncing of ("shapes", which makes is basically a NoSQL like document database) of data from a remote Postgres DB to a local clients written in TypeScript/JS or Elixir. The write path is not yet implemented, neither is client-side reactivity. The ElectricSQL backend is written in Elixir.
SignalDB provides a reactive, in-memory local-lirst JavaScript database with real-time sync, bit it doesn't offer the same level of multi-client replication or flexibility with storage backends that RxDB provides, and through a RxDB persistence adapters you can actually use SignalDB for the front-end reactivity while relying on RxDB for backend sync and persistence.
PowerSync is a "framework" for implementing local-first solutions. It centralizes business logic and conflict resolution on a central, authoritative server (PostgreSQL or MongoDB), vs RxDB that also supports custom backends. Both RxDB and PowerSync can be used with a variety of storage backends, but PowerSync uses SQLite as the front-end database which has shown to be slow because the WASM-SQLite abstraction increases read and write latency. In terms of client SDKs, PowerSync offers Flutter, Kotlin, and Swift in addition to JS/TypeScript. PowerSync offers man client technologies, PowerSync is under a license that restricts commercial use that competes with PowerSync and the JourneyApps Platform.
-
-
\ No newline at end of file
diff --git a/docs/alternatives.md b/docs/alternatives.md
deleted file mode 100644
index 10c6c4b32e8..00000000000
--- a/docs/alternatives.md
+++ /dev/null
@@ -1,236 +0,0 @@
-# Alternatives for realtime local-first JavaScript applications and local databases
-
-> Explore real-time, local-first JS alternatives to RxDB. Compare Firebase, Meteor, AWS, CouchDB, and more for robust, seamless web/mobile app development.
-
-# Alternatives for realtime offline-first JavaScript applications
-
-To give you an augmented view over the topic of client side JavaScript databases, this page contains all known alternatives to **RxDB**. Remember that you are reading this inside of the RxDB documentation, so everything is **opinionated**.
-If you disagree with anything or think that something is missing, make a pull request to this file on the RxDB github repository.
-
-:::note
-RxDB has these main benefits:
-
-- RxDB is a battle proven tool [widely used](/#reviews) by companies in real projects in production.
-- RxDB is not VC funded and therefore does not require you to use a specific cloud service to rip you off. RxDB can be used with your [own backend](./replication-http.md) or no backend at all.
-- RxDB has a working business model of selling [premium plugins](/premium/) which ensures that RxDB will be maintained and improved continuously while many alternatives are dead already or seem to die soon.
-- RxDB has years (since 2016) of performance optimization, bug fixing and feature adding. It is just working as is and there are close to zero [open issues](https://github.com/pubkey/rxdb/issues).
-
-
-
-
-
-
-
-:::
-
---------------------------------------------------------------------------------
-
-
-
-## Alternatives to RxDB
-
-[RxDB](https://rxdb.info) is an **observable**, **replicating**, **[local first](./offline-first.md)**, **JavaScript** database. So it makes only sense to list similar projects as alternatives, not just any database or JavaScript store library. However, I will list up some projects that RxDB is often compared with, even if it only makes sense for some use cases.
-Here are the alternatives to RxDB:
-
-### Firebase
-
-
-
-Firebase is a **platform** developed by Google for creating mobile and web applications. Firebase has many features and products, two of which are client side databases. The [Realtime Database](./articles/firebase-realtime-database-alternative.md) and the [Cloud Firestore](./articles/firestore-alternative.md).
-
-#### Firebase - Realtime Database
-
-The firebase realtime database was the first database in firestore. It has to be mentioned that in this context, "realtime" means **"realtime replication"**, not "realtime computing". The firebase realtime database stores data as a big unstructured JSON tree that is replicated between clients and the backend.
-
-#### Firebase - Cloud Firestore
-
-The firestore is the successor to the realtime database. The big difference is that it behaves more like a 'normal' database that stores data as documents inside of collections. The conflict resolution strategy of firestore is always *last-write-wins* which might or might not be suitable for your use case.
-
-The biggest difference to RxDB is that firebase products are only able to be used on top of the Firebase cloud hosted backend, which creates a vendor lock-in. RxDB can replicate with any self hosted CouchDB server or custom GraphQL endpoints. You can even replicate Firestore to RxDB with the [Firestore Replication Plugin](./replication-firestore.md).
-
-### Meteor
-
-
-
-Meteor (since 2012) is one of the oldest technologies for JavaScript realtime applications. Meteor is not a library but a whole framework with its own package manager, database management and replication.
-Because of how it works, it has proven to be hard to integrate it with other modern JavaScript frameworks like [angular](https://github.com/urigo/angular-meteor), [vue.js](./articles//vue-database.md) or svelte.
-
-Meteor uses MongoDB in the backend and can replicate with a Minimongo database in the frontend.
-While testing, it has proven to be impossible to make a meteor app **offline first** capable. There are [some projects](https://github.com/frozeman/meteor-persistent-minimongo2) that might do this, but all are unmaintained.
-
-### Minimongo
-
-Forked in Jan 2014 from meteorJSs' minimongo package, Minimongo is a client-side, in-memory, JavaScript version of MongoDB with backend replication over HTTP. Similar to MongoDB, it stores data in documents inside of collections and also has the same query syntax. Minimongo has different storage adapters for IndexedDB, WebSQL, [LocalStorage](./articles/localstorage.md) and SQLite.
-Compared to RxDB, Minimongo has no concept of revisions or conflict handling, which might lead to undefined behavior when used with replication or in multiple browser tabs. Minimongo has no observable queries or changestream.
-
-### WatermelonDB
-
-
-
-WatermelonDB is a reactive & asynchronous JavaScript database. While originally made for [React](./articles/react-database.md) and [React Native](./react-native-database.md), it can also be used with other JavaScript frameworks. The main goal of WatermelonDB is **performance** within an application with lots of data.
-In React Native, WatermelonDB uses the provided SQLite database. Also there is an Expo plugin for WatermelonDB. In a browser, WatermelonDB uses the LokiJS in-memory database to store and query data. WatermelonDB is one of the rare projects that support both Flow and Typescript at the same time.
-
-### AWS Amplify
-
-
-
-AWS Amplify is a collection of tools and libraries to develop web- and mobile frontend applications. Similar to firebase, it provides everything needed like authentication, analytics, a REST API, storage and so on. Everything hosted in the AWS Cloud, even when they state that *"AWS Amplify is designed to be open and pluggable for any custom backend or service"*. For realtime replication, AWS Amplify can connect to an AWS App-Sync GraphQL endpoint.
-
-### AWS Datastore
-
-Since December 2019 the Amplify library includes the AWS Datastore which is a document-based, client side database that is able to replicate data via AWS AppSync in the background.
-The main difference to other projects is the complex project configuration via the amplify cli and the bit confusing query syntax that works over functions. Complex Queries with multiple `OR/AND` statements are not possible which might change in the future.
-Local development is hard because the AWS AppSync mock does not support realtime replication. It also is not really offline-first because a user login is always required.
-
-```ts
-// An AWS datastore OR query
-const posts = await DataStore.query(Post, c => c.or(
- c => c.rating("gt", 4).status("eq", PostStatus.PUBLISHED)
-));
-
-// An AWS datastore SORT query
-const posts = await DataStore.query(Post, Predicates.ALL, {
- sort: s => s.rating(SortDirection.ASCENDING).title(SortDirection.DESCENDING)
-});
-```
-
-The biggest difference to RxDB is that you have to use the AWS cloud backends. This might not be a problem if your data is at AWS anyway.
-
-### RethinkDB
-
-
-
-RethinkDB is a backend database that pushed dynamic JSON data to the client in realtime. It was founded in 2009 and the company shut down in 2016.
-Rethink db is not a client side database, it streams data from the backend to the client which of course does not work while offline.
-
-### Horizon
-
-Horizon is the client side library for RethinkDB which provides useful functions like authentication, permission management and subscription to a RethinkDB backend. Offline support [never made](https://github.com/rethinkdb/horizon/issues/58) it to horizon.
-
-### Supabase
-
-
-
-Supabase labels itself as "*an open source Firebase alternative*". It is a collection of open source tools that together mimic many Firebase features, most of them by providing a wrapper around a PostgreSQL database. While it has realtime queries that run over the wire, like with RethinkDB, Supabase has no client-side storage or replication feature and therefore is not offline first.
-
-### CouchDB
-
-
-
-Apache CouchDB is a server-side, document-oriented database that is mostly known for its multi-master replication feature. Instead of having a master-slave replication, with CouchDB you can run replication in any constellation without having a master server as bottleneck where the server even can go off- and online at any time. This comes with the drawback of having a slow replication with much network overhead.
-CouchDB has a changestream and a query syntax similar to MongoDB.
-
-### PouchDB
-
-
-
-PouchDB is a JavaScript database that is compatible with most of the CouchDB API. It has an adapter system that allows you to switch out the underlying storage layer. There are many adapters like for [IndexedDB](./rx-storage-indexeddb.md), [SQLite](./rx-storage-sqlite.md), the Filesystem and so on. The main benefit is to be able to replicate data with any CouchDB compatible endpoint.
-Because of the CouchDB compatibility, PouchDB has to do a lot of overhead in handling the revision tree of document, which is why it can show bad performance for bigger datasets.
-RxDB was originally build around PouchDB until the storage layer was abstracted out in version [10.0.0](./releases/10.0.0.md) so it now allows to use different `RxStorage` implementations. PouchDB has some performance issues because of how it has to store the document revision tree to stay compatible with the CouchDB API.
-
-### Couchbase
-
-Couchbase (originally known as Membase) is another NoSQL document database made for realtime applications.
-It uses the N1QL query language which is more SQL like compared to other NoSQL query languages. In theory you can achieve replication of a Couchbase with a PouchDB database, but this has shown to be not [that easy](https://github.com/pouchdb/pouchdb/issues/7793#issuecomment-501624297).
-
-### Cloudant
-
-Cloudant is a cloud-based service that is based on [CouchDB](./replication-couchdb.md) and has mostly the same features.
-It was originally designed for cloud computing where data can automatically be distributed between servers. But it can also be used to replicate with frontend PouchDB instances to create scalable web applications.
-It was bought by IBM in 2014 and since 2018 the Cloudant Shared Plan is retired and migrated to IBM Cloud.
-
-### Hoodie
-
-Hoodie is a backend solution that enables offline-first JavaScript frontend development without having to write backend code. Its main goal is to abstract away configuration into simple calls to the Hoodie API.
-It uses CouchDB in the backend and PouchDB in the frontend to enable offline-first capabilities.
-The last commit for hoodie was one year ago and the website (hood.ie) is offline which indicates it is not an active project anymore.
-
-### LokiJS
-
-LokiJS is a JavaScript embeddable, in-memory database. And because everything is handled in-memory, LokiJS has awesome performance when mutating or querying data. You can still persist to a permanent storage (IndexedDB, Filesystem etc.) with one of the provided storage adapters. The persistence happens after a timeout is reached after a write, or before the JavaScript process exits. This also means you could loose data when the JavaScript process exits ungracefully like when the power of the device is shut down or the browser crashes.
-While the project is not that active anymore, it is more *finished* than *unmaintained*.
-
-In the past, RxDB supported using [LokiJS as RxStorage](./rx-storage-lokijs.md) but because the LokiJS is not maintained anymore and had too many issues, this storage option was removed in RxDB version 16.
-
-### Gundb
-
-GUN is a JavaScript graph database. While having many features, the **decentralized** replication is the main unique selling point. You can replicate data Peer-to-Peer without any centralized backend server. GUN has several other features that are useful on top of that, like encryption and authentication.
-
-While testing it was really hard to get basic things running. GUN is open source, but because of how the source code [is written](https://github.com/amark/gun/blob/master/src/put.js), it is very difficult to understand what is going wrong.
-
-### sql.js
-
-sql.js is a javascript library to run SQLite on the web. It uses a virtual database file stored in memory and does not have any persistence. All data is lost once the JavaScript process exits. sql.js is created by compiling SQLite to WebAssembly so it has about the same features as SQLite. For older browsers there is a JavaScript fallback.
-
-### absurd-sQL
-
-Absurd-sql is a project that implements an IndexedDB-based persistence for sql.js. Instead of directly writing data into the IndexedDB, it treats IndexedDB like a disk and stores data in blocks there which shows to have a much better performance, mostly because of how [performance expensive](./slow-indexeddb.md) IndexedDB transactions are.
-
-### NeDB
-
-NeDB was a embedded persistent or in-memory database for Node.js, nw.js, [Electron](./electron-database.md) and browsers.
-It is document-oriented and had the same query syntax as MongoDB.
-Like LokiJS it has persistence adapters for IndexedDB etc. to persist the database state on the disc.
-The last commit to NeDB was in **2016**.
-
-### Dexie.js
-
-Dexie.js is a minimalistic wrapper for IndexedDB. While providing a better API than plain IndexedDB, Dexie also improves performance by batching transactions and other optimizations. It also adds additional non-IndexedDB features like observable queries or multi tab support or react hooks.
-Compared to RxDB, Dexie.js does not support complex (MongoDB-like) queries and requires a lot of fiddling when a document range of a specific index must be fetched.
-Dexie.js is used by Whatsapp Web, Microsoft To Do and Github Desktop.
-
-RxDB supports using [Dexie.js as Database storage](./rx-storage-dexie.md) which enhances IndexedDB via dexie with RxDB features like MongoDB-like queries etc.
-
-### LowDB
-
-LowDB is a small, local JSON database powered by the Lodash library. It is designed to be simple, easy to use, and straightforward. LowDB allows you to perform native JavaScript queries and persist data in a flat JSON file. Written in TypeScript, it's particularly well-suited for small projects, prototyping, or when you need a lightweight, file-based database.
-
-As an alternative to LowDB, [RxDB](./) offers real-time reactivity, allowing developers to subscribe to database changes, a feature not natively available in LowDB. Additionally, RxDB provides robust [query capabilities](./rx-query.md), including the ability to subscribe to query results for automatic UI updates. These features make RxDB a strong alternative to LowDB for more complex and dynamic applications.
-
-### localForage
-localForage is a popular JavaScript library for offline storage that provides a simple, promise-based API. It abstracts over different storage mechanisms such as [IndexedDB](./rx-storage-indexeddb.md), WebSQL, or [localStorage](./articles/localstorage.md), making it easier to write code once and have it work seamlessly across various browsers. While localForage is great for storing data locally in a key-value manner, it doesn't provide the real-time reactive queries, [conflict handling](./transactions-conflicts-revisions.md), or revision-based replication that RxDB does. This makes localForage a useful choice for straightforward caching or persistent storage needs, but not ideal for advanced offline-first scenarios requiring multi-user collaboration or complex querying.
-
-### MongoDB Realm
-
-Originally Realm was a mobile database for Android and iOS. Later they added support for other languages and runtimes, also for JavaScript.
-It was meant as replacement for SQLite but is more like an object store than a full SQL database.
-In 2019 MongoDB bought Realm and changed the projects focus.
-Now Realm is made for replication with the MongoDB Realm Sync based on the MongoDB Atlas Cloud platform. This tight coupling to the MongoDB cloud service is a big downside for most use cases.
-
-### Apollo
-
-The Apollo GraphQL platform is made to transfer data between a server to UI applications over GraphQL endpoints. It contains several tools like GraphQL clients in different languages or libraries to create GraphQL endpoints.
-
-While it is has different caching features for offline usage, compared to RxDB it is not fully offline first because caching alone does not mean your application is fully usable when the user is offline.
-
-### Replicache
-
-Replicache is a client-side sync framework for building realtime, collaborative, [local-first](./articles/local-first-future.md) web apps. It claims to work with most backend stacks. In contrast to other local first tools, replicache does not work like a local database. Instead it runs on so called `mutators` that unify behavior on the client and server side. So instead of implementing and calling REST routes on both sides of your stack, you will implement mutators that define a specific delta behavior based on the input data. To observe data in replicache, there are `subscriptions` that notify your frontend application about changes to the state.
-Replicache can be used in most frontend technologies like browsers, React/Remix, NextJS/Vercel and React Native. While Replicache can be installed and used from npm, the Replicache source code is not open source and the Replicache github repo does not allow you to inspect or debug it. Still you can use replicache for in non-commercial projects, or for companies with < $200k revenue (ARR) and < $500k in funding. (2024: Replicache will be free and Rocicorp are working on a new Zerosync product to succeed Replicache and Reflect.)
-
-### InstantDB
-
-InstantDB is designed for real-time data synchronization with built-in offline support, allowing changes to be queued locally and [synced](./replication.md) when the user reconnects. While it offers seamless [optimistic updates](./articles/optimistic-ui.md) and rollback capabilities, its offline-first design is not as mature or comprehensive as RxDB's - the [offline data](./articles//offline-database.md) is more of a cache, not a full-database sync. The query language used is Datalog, and the backend sync service is written in Clojure. InstantDB is focused more on simplicity and real-time collaboration, with fewer customization options for storage or conflict resolution compared to RxDB, which supports various storage adapters and advanced conflict handling via CRDTs.
-
-### Yjs
-
-Yjs is a [CRDT-based](./crdt.md) (Conflict-free Replicated Data Type) library focused on enabling real-time collaboration - particularly for text editing, although it can handle other data types as well. While it provides powerful conflict resolution and peer-to-peer synchronization out of the box, Yjs itself is not a full-fledged database. Instead, you typically combine Yjs with other storage or networking layers to achieve a [local-first architecture](./offline-first.md). This flexibility allows for sophisticated [real-time](./articles/realtime-database.md) features, but also means you must handle indexing, queries, and persistence on your own if you need them. Compared to RxDB, Yjs does not offer built-in replication adapters or a query system, so developers who require a more complete solution for conflict resolution, data persistence, and offline-first capabilities may find RxDB more convenient.
-
-### ElectricSQL
-
-2024: ElectricSQL is being rewritten in a new Electric-Next branch, which focuses on partial syncing of ("shapes", which makes is basically a NoSQL like document database) of data from a remote Postgres DB to a local clients written in TypeScript/JS or Elixir. The write path is not yet implemented, neither is client-side reactivity. The ElectricSQL backend is written in Elixir.
-
-### SignalDB
-
-SignalDB provides a reactive, in-memory local-lirst JavaScript database with real-time sync, bit it doesn't offer the same level of multi-client replication or flexibility with storage backends that RxDB provides, and through a RxDB persistence adapters you can actually use SignalDB for the front-end reactivity while relying on RxDB for backend sync and persistence.
-
-### PowerSync
-
-PowerSync is a "framework" for implementing local-first solutions. It centralizes business logic and conflict resolution on a central, authoritative server (PostgreSQL or MongoDB), vs RxDB that also supports custom backends. Both RxDB and PowerSync can be used with a variety of storage backends, but PowerSync uses SQLite as the front-end database which has shown to be slow because the WASM-SQLite abstraction increases read and write latency. In terms of client SDKs, PowerSync offers Flutter, Kotlin, and Swift in addition to JS/TypeScript. PowerSync offers man client technologies, PowerSync is under a license that restricts commercial use that competes with PowerSync and the JourneyApps Platform.
-
-# Read further
-
-- [Offline First Database Comparison](https://github.com/pubkey/client-side-databases)
diff --git a/docs/articles/angular-database.html b/docs/articles/angular-database.html
deleted file mode 100644
index 7d9b43694ef..00000000000
--- a/docs/articles/angular-database.html
+++ /dev/null
@@ -1,175 +0,0 @@
-
-
-
-
-
-RxDB as a Database in an Angular Application | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In modern web development, Angular has emerged as a popular framework for building robust and scalable applications. As Angular applications often require persistent storage and efficient data handling, choosing the right database solution is crucial. One such solution is RxDB, a reactive JavaScript database for the browser, node.js, and mobile devices. In this article, we will explore the integration of RxDB into an Angular application and examine its various features and techniques.
Angular is a powerful JavaScript framework developed and maintained by Google. It enables developers to build single-page applications (SPAs) with a modular and component-based approach. Angular provides a comprehensive set of tools and features for creating dynamic and responsive web applications.
Databases play a vital role in Angular applications by providing a structured and efficient way to store, retrieve, and manage data. Whether it's handling user authentication, caching data, or persisting application state, a robust database solution is essential for ensuring optimal performance and user experience.
RxDB stands for Reactive Database and is built on the principles of reactive programming. It combines the best features of NoSQL databases with the power of reactive programming to provide a scalable and efficient database solution. RxDB offers seamless integration with Angular applications and brings several unique features that make it an attractive choice for developers.
-
3:45
This solved a problem I've had in Angular for years
RxDB is a client-side database that follows the principles of reactive programming. It is built on top of IndexedDB, the native browser database, and leverages the RxJS library for reactive data handling. RxDB provides a simple and intuitive API for managing data and offers features like data replication, multi-tab support, and efficient query handling.
At the core of RxDB is the concept of reactive data handling. RxDB leverages observables and reactive streams to enable real-time updates and data synchronization. With RxDB, you can easily subscribe to data changes and react to them in a reactive and efficient manner.
One of the standout features of RxDB is its offline-first approach. It allows you to build applications that can work seamlessly in offline scenarios. RxDB stores data locally and automatically synchronizes changes with the server when the network becomes available. This capability is particularly useful for applications that need to function in low-connectivity or unreliable network environments.
RxDB provides built-in support for data replication between clients and servers. This means you can synchronize data across multiple devices or instances of your application effortlessly. RxDB handles conflict resolution and ensures that data remains consistent across all connected clients.
RxDB offers a powerful querying mechanism with support for observable queries. This allows you to create dynamic queries that automatically update when the underlying data changes. By leveraging RxDB's observable queries, you can build reactive UI components that respond to data changes in real-time.
RxDB provides out-of-the-box support for multi-tab scenarios. This means that if your Angular application is running in multiple browser tabs, RxDB automatically keeps the data in sync across all tabs. It ensures that changes made in one tab are immediately reflected in others, providing a seamless user experience.
While there are other database options available for Angular applications, RxDB stands out with its reactive programming model, offline-first approach, and built-in synchronization capabilities. Unlike traditional SQL databases, RxDB's NoSQL-like structure and observables-based API make it well-suited for real-time applications and complex data scenarios.
To use RxDB in an Angular application, we first need to install the necessary dependencies. You can install RxDB using npm or yarn by running the following command:
-
npm install rxdb --save
-
Once installed, you can import RxDB into your Angular application and start using its API to create and manage databases.
Angular uses change detection to detect and update UI elements when data changes. However, RxDB's data handling is based on observables, which can sometimes bypass Angular's change detection mechanism. To ensure that changes made in RxDB are detected by Angular, we need to patch the change detection mechanism using zone.js. Zone.js is a library that intercepts and tracks asynchronous operations, including observables. By patching zone.js, we can make sure that Angular is aware of changes happening in RxDB.
-
warning
RxDB creates rxjs observables outside of angulars zone
-So you have to import the rxjs patch to ensure the angular change detection works correctly.
-link
Use the Angular async pipe to observe an RxDB Query
-
Angular provides the async pipe, which is a convenient way to subscribe to observables and handle the subscription lifecycle automatically. When working with RxDB, you can use the async pipe to observe an RxDB query and bind the results directly to your Angular template. This ensures that the UI stays in sync with the data changes emitted by the RxDB query.
IndexedDB RxStorage: RxDB directly supports IndexedDB as a storage layer. IndexedDB is a low-level browser database that offers good performance and reliability.
-
OPFS RxStorage: The OPFS RxStorage for RxDB is built on top of the File System Access API which is available in all modern browsers. It provides an API to access a sandboxed private file system to persistently store and retrieve data.
-Compared to other persistent storage options in the browser (like IndexedDB), the OPFS API has a way better performance.
-
Memory RxStorage: In addition to persistent storage options, RxDB also provides a memory-based storage layer. This is useful for testing or scenarios where you don't need long-term data persistence.
-You can choose the storage layer that best suits your application's requirements and configure RxDB accordingly.
-
-
Synchronizing Data with RxDB between Clients and Servers
-
Data replication between an Angular application and a server is a common requirement. RxDB simplifies this process and provides built-in support for data synchronization. Let's explore how to replicate data between an Angular application and a server using RxDB.
One of the key strengths of RxDB is its offline-first approach. It allows Angular applications to function seamlessly even in offline scenarios. RxDB stores data locally and automatically synchronizes changes with the server when the network becomes available. This capability is particularly useful for applications that need to operate in low-connectivity or unreliable network environments.
In a distributed system, conflicts can arise when multiple clients modify the same data simultaneously. RxDB offers conflict resolution mechanisms to handle such scenarios. You can define conflict resolution strategies based on your application's requirements. RxDB provides hooks and events to detect conflicts and resolve them in a consistent manner.
RxDB supports bidirectional data synchronization, allowing updates from both the client and server to be replicated seamlessly. This ensures that data remains consistent across all connected clients and the server. RxDB handles conflicts and resolves them based on the defined conflict resolution strategies.
RxDB provides real-time updates by leveraging reactive programming principles. Changes made to the data are automatically propagated to all connected clients in real-time. Angular applications can subscribe to these updates and update the user interface accordingly. This real-time capability enables collaborative features and enhances the overall user experience.
To improve query performance, RxDB allows you to define indexes on specific fields of your documents. Indexing enables faster data retrieval and query execution, especially when working with large datasets. By strategically creating indexes, you can optimize the performance of your Angular application.
RxDB provides built-in support for encrypting local data using the Web Crypto API. With encryption, you can protect sensitive data stored in the client-side database. RxDB transparently encrypts the data, ensuring that it remains secure even if the underlying storage is compromised.
RxDB exposes change streams, which allow you to listen for data changes at a database or collection level. By subscribing to change streams, you can react to data modifications and perform specific actions, such as updating the UI or triggering notifications. Change streams enable real-time event handling in your Angular application.
To reduce the storage footprint and improve performance, RxDB supports JSON key compression. With key compression, RxDB replaces long keys with shorter aliases, reducing the overall storage size. This optimization is particularly useful when working with large datasets or frequently updating data.
-
Best Practices for Using RxDB in Angular Applications
-
To make the most of RxDB in your Angular application, consider the following best practices:
-
Use Async Pipe for Subscriptions so you do not have to unsubscribe
-
Angular's async pipe is a powerful tool for handling observables in templates. By using the async pipe, you can avoid the need to manually subscribe and unsubscribe from RxDB observables. Angular takes care of the subscription lifecycle, ensuring that resources are released when they are no longer needed. Instead of manually subscribing to Observables, you should always prefer the async pipe.
To ensure proper separation of concerns and maintain a clean codebase, it is recommended to create an Angular service responsible for managing the RxDB database instance. This service can handle database creation, initialization, and provide methods for interacting with the database throughout your application.
RxDB provides various mechanisms for efficient data handling, such as batching updates, debouncing, and throttling. Leveraging these techniques can help optimize performance and reduce unnecessary UI updates. Consider the specific data handling requirements of your application and choose the appropriate strategies provided by RxDB.
When working with data synchronization between clients and servers, it's important to consider strategies for conflict resolution and handling network failures. RxDB provides plugins and hooks that allow you to customize the replication behavior and implement specific synchronization strategies tailored to your application's needs.
RxDB is a powerful database solution for Angular applications, offering reactive data handling, offline-first capabilities, and seamless data synchronization. By integrating RxDB into your Angular application, you can build responsive and scalable web applications that provide a rich user experience. Whether you're building real-time collaborative apps, progressive web applications, or offline-capable applications, RxDB's features and techniques make it a valuable addition to your Angular development toolkit.
To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-
-
\ No newline at end of file
diff --git a/docs/articles/angular-database.md b/docs/articles/angular-database.md
deleted file mode 100644
index 2e0c47410ae..00000000000
--- a/docs/articles/angular-database.md
+++ /dev/null
@@ -1,214 +0,0 @@
-# RxDB as a Database in an Angular Application
-
-> Level up your Angular projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.
-
-import {VideoBox} from '@site/src/components/video-box';
-
-# RxDB as a Database in an Angular Application
-
-In modern web development, Angular has emerged as a popular framework for building robust and scalable applications. As Angular applications often require persistent [storage](./browser-storage.md) and efficient data handling, choosing the right database solution is crucial. One such solution is [RxDB](https://rxdb.info/), a reactive JavaScript database for the browser, node.js, and [mobile devices](./mobile-database.md). In this article, we will explore the integration of RxDB into an Angular application and examine its various features and techniques.
-
-
-
-
-
-
-
-## Angular Web Applications
-Angular is a powerful JavaScript framework developed and maintained by Google. It enables developers to build single-page applications (SPAs) with a modular and component-based approach. Angular provides a comprehensive set of tools and features for creating dynamic and responsive web applications.
-
-## Importance of Databases in Angular Applications
-Databases play a vital role in Angular applications by providing a structured and efficient way to store, retrieve, and manage data. Whether it's handling user authentication, caching data, or persisting application state, a robust database solution is essential for ensuring optimal performance and user experience.
-
-## Introducing RxDB as a Database Solution
-RxDB stands for Reactive Database and is built on the principles of reactive programming. It combines the best features of [NoSQL databases](./in-memory-nosql-database.md) with the power of reactive programming to provide a scalable and efficient database solution. RxDB offers seamless integration with Angular applications and brings several unique features that make it an attractive choice for developers.
-
-
-
-
-
-## Getting Started with RxDB
-To begin our journey with RxDB, let's understand its key concepts and features.
-
-### What is RxDB?
-[RxDB](https://rxdb.info/) is a client-side database that follows the principles of reactive programming. It is built on top of IndexedDB, the [native browser database](./browser-database.md), and leverages the RxJS library for reactive data handling. RxDB provides a simple and intuitive API for managing data and offers features like data replication, multi-tab support, and efficient query handling.
-
-
-
-
-
-
-
-### Reactive Data Handling
-At the core of RxDB is the concept of reactive data handling. RxDB leverages observables and reactive streams to enable real-time updates and data synchronization. With RxDB, you can easily subscribe to data changes and react to them in a reactive and efficient manner.
-
-
-
-### Offline-First Approach
-One of the standout features of RxDB is its offline-first approach. It allows you to build applications that can work seamlessly in offline scenarios. RxDB stores data locally and automatically synchronizes changes with the server when the network becomes available. This capability is particularly useful for applications that need to function in low-connectivity or unreliable network environments.
-
-### Data Replication
-RxDB provides built-in support for data replication between clients and servers. This means you can synchronize data across multiple devices or instances of your application effortlessly. RxDB handles conflict resolution and ensures that data remains consistent across all connected clients.
-
-### Observable Queries
-RxDB offers a powerful querying mechanism with support for observable queries. This allows you to create dynamic queries that automatically update when the underlying data changes. By leveraging RxDB's observable queries, you can build reactive UI components that respond to data changes in real-time.
-
-### Multi-Tab Support
-RxDB provides out-of-the-box support for multi-tab scenarios. This means that if your Angular application is running in multiple browser tabs, RxDB automatically keeps the data in sync across all tabs. It ensures that changes made in one tab are immediately reflected in others, providing a seamless user experience.
-
-
-
-### RxDB vs. Other Angular Database Options
-While there are other database options available for Angular applications, RxDB stands out with its reactive programming model, offline-first approach, and built-in synchronization capabilities. Unlike traditional SQL databases, RxDB's NoSQL-like structure and observables-based API make it well-suited for real-time applications and complex data scenarios.
-
-## Using RxDB in an Angular Application
-Now that we have a good understanding of RxDB and its features, let's explore how to integrate it into an Angular application.
-
-### Installing RxDB in an Angular App
-To use RxDB in an Angular application, we first need to install the necessary dependencies. You can install RxDB using npm or yarn by running the following command:
-
-```bash
-npm install rxdb --save
-```
-Once installed, you can import RxDB into your Angular application and start using its API to create and manage databases.
-
-### Patch Change Detection with zone.js
-Angular uses change detection to detect and update UI elements when data changes. However, RxDB's data handling is based on observables, which can sometimes bypass Angular's change detection mechanism. To ensure that changes made in RxDB are detected by Angular, we need to patch the change detection mechanism using zone.js. Zone.js is a library that intercepts and tracks asynchronous operations, including observables. By patching zone.js, we can make sure that Angular is aware of changes happening in RxDB.
-
-:::warning
-
-RxDB creates rxjs observables outside of angulars zone
-So you have to import the rxjs patch to ensure the [angular change detection](https://angular.io/guide/change-detection) works correctly.
-[link](https://www.bennadel.com/blog/3448-binding-rxjs-observable-sources-outside-of-the-ngzone-in-angular-6-0-2.htm)
-
-```ts
-//> app.component.ts
-import 'zone.js/plugins/zone-patch-rxjs';
-```
-:::
-
-### Use the Angular async pipe to observe an RxDB Query
-Angular provides the async pipe, which is a convenient way to subscribe to observables and handle the subscription lifecycle automatically. When working with RxDB, you can use the async pipe to observe an RxDB query and bind the results directly to your Angular template. This ensures that the UI stays in sync with the data changes emitted by the RxDB query.
-
-```ts
- constructor(
- private dbService: DatabaseService,
- private dialog: MatDialog
- ) {
- this.heroes$ = this.dbService
- .db.hero // collection
- .find({ // query
- selector: {},
- sort: [{ name: 'asc' }]
- })
- .$;
- }
-```
-
-```html
-
- {{hero.name}}
-
-```
-
-### Different RxStorage layers for RxDB
-RxDB supports multiple storage layers for persisting data. Some of the available storage options include:
-
-- [LocalStorage RxStorage](../rx-storage-localstorage.md): Uses the [LocalStorage API](./localstorage.md) without any third party plugins.
-- [IndexedDB RxStorage](../rx-storage-indexeddb.md): RxDB directly supports IndexedDB as a storage layer. IndexedDB is a low-level browser database that offers good performance and reliability.
-- [OPFS RxStorage](../rx-storage-opfs.md): The OPFS [RxStorage](../rx-storage.md) for RxDB is built on top of the [File System Access API](https://webkit.org/blog/12257/the-file-system-access-api-with-origin-private-file-system/) which is available in [all modern browsers](https://caniuse.com/native-filesystem-api). It provides an API to access a sandboxed private file system to persistently store and retrieve data.
-Compared to other persistent storage options in the browser (like [IndexedDB](../rx-storage-indexeddb.md)), the OPFS API has a **way better performance**.
-- [Memory RxStorage](../rx-storage-memory.md): In addition to persistent storage options, RxDB also provides a memory-based storage layer. This is useful for testing or scenarios where you don't need long-term data persistence.
-You can choose the storage layer that best suits your application's requirements and configure RxDB accordingly.
-
-## Synchronizing Data with RxDB between Clients and Servers
-
-Data replication between an Angular application and a server is a common requirement. RxDB simplifies this process and provides built-in support for data synchronization. Let's explore how to replicate data between an Angular application and a server using RxDB.
-
-
-
-### Offline-First Approach
-One of the key strengths of RxDB is its [offline-first approach](../offline-first.md). It allows Angular applications to function seamlessly even in offline scenarios. RxDB stores data locally and automatically synchronizes changes with the server when the network becomes available. This capability is particularly useful for applications that need to operate in low-connectivity or unreliable network environments.
-
-### Conflict Resolution
-In a distributed system, conflicts can arise when multiple clients modify the same data simultaneously. RxDB offers conflict resolution mechanisms to handle such scenarios. You can define conflict resolution strategies based on your application's requirements. RxDB provides hooks and events to detect conflicts and resolve them in a consistent manner.
-
-### Bidirectional Synchronization
-RxDB supports bidirectional data synchronization, allowing updates from both the client and server to be replicated seamlessly. This ensures that data remains consistent across all connected clients and the server. RxDB handles conflicts and resolves them based on the defined conflict resolution strategies.
-
-### Real-Time Updates
-RxDB provides real-time updates by leveraging reactive programming principles. Changes made to the data are automatically propagated to all connected clients in real-time. Angular applications can subscribe to these updates and update the user interface accordingly. This real-time capability enables collaborative features and enhances the overall user experience.
-
-## Advanced RxDB Features and Techniques
-RxDB offers several advanced features and techniques that can further enhance your Angular application.
-
-### Indexing and Performance Optimization
-To improve query performance, RxDB allows you to define indexes on specific fields of your documents. Indexing enables faster data retrieval and query execution, especially when working with large datasets. By strategically creating indexes, you can optimize the performance of your Angular application.
-
-### Encryption of Local Data
-RxDB provides built-in support for [encrypting](../encryption.md) local data using the Web Crypto API. With encryption, you can protect sensitive data stored in the client-side database. RxDB transparently encrypts the data, ensuring that it remains secure even if the underlying storage is compromised.
-
-### Change Streams and Event Handling
-RxDB exposes change streams, which allow you to listen for data changes at a database or collection level. By subscribing to change streams, you can react to data modifications and perform specific actions, such as updating the UI or triggering notifications. Change streams enable real-time event handling in your Angular application.
-
-### JSON Key Compression
-To reduce the storage footprint and improve performance, RxDB supports [JSON key compression](../key-compression.md). With key compression, RxDB replaces long keys with shorter aliases, reducing the overall storage size. This optimization is particularly useful when working with large datasets or frequently updating data.
-
-## Best Practices for Using RxDB in Angular Applications
-To make the most of RxDB in your Angular application, consider the following best practices:
-
-### Use Async Pipe for Subscriptions so you do not have to unsubscribe
-Angular's `async` pipe is a powerful tool for handling observables in templates. By using the async pipe, you can avoid the need to manually subscribe and unsubscribe from RxDB observables. Angular takes care of the subscription lifecycle, ensuring that resources are released when they are no longer needed. Instead of manually subscribing to Observables, you should always prefer the `async` pipe.
-
-```ts
-// WRONG:
-let amount;
-this.dbService
- .db.hero
- .find({
- selector: {},
- sort: [{ name: 'asc' }]
- })
- .$.subscribe(docs => {
- amount = 0;
- docs.forEach(d => amount = d.points);
- });
-
-// RIGHT:
-this.amount$ = this.dbService
- .db.hero
- .find({
- selector: {},
- sort: [{ name: 'asc' }]
- })
- .$.pipe(
- map(docs => {
- let amount = 0;
- docs.forEach(d => amount = d.points);
- return amount;
- })
- );
-```
-
-### Use custom reactivity to have signals instead of rxjs observables
-
-RxDB supports adding custom reactivity factories that allow you to get angular signals out of the database instead of rxjs observables. [read more](../reactivity.md).
-
-### Use Angular Services for Database creation
-To ensure proper separation of concerns and maintain a clean codebase, it is recommended to create an Angular service responsible for managing the RxDB database instance. This service can handle database creation, initialization, and provide methods for interacting with the database throughout your application.
-
-### Efficient Data Handling
-RxDB provides various mechanisms for efficient data handling, such as batching updates, debouncing, and throttling. Leveraging these techniques can help optimize performance and reduce unnecessary UI updates. Consider the specific data handling requirements of your application and choose the appropriate strategies provided by RxDB.
-
-### Data Synchronization Strategies
-When working with data synchronization between clients and servers, it's important to consider strategies for conflict resolution and handling network failures. RxDB provides plugins and hooks that allow you to customize the replication behavior and implement specific synchronization strategies tailored to your application's needs.
-
-## Conclusion
-RxDB is a powerful database solution for Angular applications, offering reactive data handling, offline-first capabilities, and seamless data synchronization. By integrating RxDB into your Angular application, you can build responsive and scalable web applications that provide a rich user experience. Whether you're building real-time collaborative apps, progressive web applications, or offline-capable applications, RxDB's features and techniques make it a valuable addition to your Angular development toolkit.
-
-## Follow Up
-To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-- [RxDB Angular Example at GitHub](https://github.com/pubkey/rxdb/tree/master/examples/angular)
diff --git a/docs/articles/angular-indexeddb.html b/docs/articles/angular-indexeddb.html
deleted file mode 100644
index eb6ad5ccfe5..00000000000
--- a/docs/articles/angular-indexeddb.html
+++ /dev/null
@@ -1,269 +0,0 @@
-
-
-
-
-
-Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Build Smarter Offline-First Angular Apps: How RxDB Beats IndexedDB Alone
-
In modern web applications, offline capabilities and fast interactions are crucial. IndexedDB, the browser's built-in database, allows you to store data locally, making your Angular application more robust and responsive. However, IndexedDB can be cumbersome to work with directly. That's where RxDB (Reactive Database) shines. In this article, we'll walk you through how to utilize IndexedDB in your Angular project using RxDB as a convenient abstraction layer.
IndexedDB is a low-level JavaScript API for client-side storage of large amounts of structured data. It allows you to create key-value or object store-based data storage right in the user's browser. IndexedDB supports transactions and indexing but lacks a robust query API and can be complex to use due to its callback-based nature.
Offline-First/Local-First: If your app needs to function with limited or no internet connectivity, IndexedDB provides a reliable local storage layer. Users can continue using the application offline, and data can sync when the connection is restored.
-
-
-
Performance: Local data access comes with near-zero latency, removing the need for constant server requests and eliminating most loading spinners.
-
-
-
Easier to Implement: By replicating all necessary data to the client once, you avoid implementing numerous backend endpoints for each user interaction.
-
-
-
Scalability: Local data queries remove processing load from your servers and reduce bandwidth usage by handling queries on the client side.
Despite the advantages, directly working with IndexedDB has several drawbacks:
-
-
-
Callback-Based: IndexedDB was originally designed around a callback-based API, which can be unwieldy compared to modern Promise or RxJS-based flows.
-
-
-
Difficult to Implement: IndexedDB is often described as a "low-level" API. It's more suitable for library authors rather than application developers who simply need a robust local store.
-
-
-
Rudimentary Query API: Complex or dynamic queries are cumbersome with IndexedDB's basic get/put approach and limited indexes.
-
-
-
TypeScript Support: Maintaining strong TypeScript types for all document structures is not straightforward with IndexedDB's untyped object stores.
-
-
-
No Observable API: IndexedDB cannot directly emit live data changes. With RxDB, you can subscribe to changes on a collection or even a single document field.
-
-
-
Cross-Tab Synchronization: Handling concurrent data changes across multiple browser tabs is difficult in IndexedDB. RxDB has built-in multi-tab support that keeps all tabs in sync.
-
-
-
Advanced Features Missing: IndexedDB lacks built-in support for encryption, compression, or other advanced data management features.
-
-
-
Browser-Only: IndexedDB works in the browser but not in environments like React Native or Electron. RxDB offers storage adapters to seamlessly reuse the same code on different platforms.
RxDB creates RxJS observables outside of Angular's zone, meaning Angular won't automatically trigger change detection when new data arrives. You must patch RxJS with zone.js:
-
//> app.component.ts
-/**
- * IMPORTANT: RxDB creates rxjs observables outside of Angular's zone
- * So you have to import the rxjs patch to ensure change detection works correctly.
- * @link https://www.bennadel.com/blog/3448-binding-rxjs-observable-sources-outside-of-the-ngzone-in-angular-6-0-2.htm
- */
-import 'zone.js/plugins/zone-patch-rxjs';
RxDB supports multiple storage options. The free and simple approach is using the localstorage-based storage. For higher performance, there's a premium plain IndexedDB storage.
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-export async function initDB() {
- // Create a database
- const db = await createRxDatabase({
- name: 'heroesdb', // the name of the database
- storage: getRxStorageLocalstorage()
- });
-
- // Add collections
- await db.addCollections({
- heroes: {
- schema: heroSchema
- }
- });
-
- return db;
-}
-
It's recommended to encapsulate database creation logic in an Angular service, such as in a DatabaseService. A full example is available in RxDB's Angular example.
A key benefit of RxDB is reactivity. You can subscribe to changes and have your UI automatically reflect updates in real time even across browser tabs.
Angular Signals are a newer approach for reactivity. RxDB supports them via a custom reactivity factory. You can convert RxJS Observables to Signals using Angular's toSignal:
A comprehensive example of RxDB in an Angular application is available in the RxDB GitHub repository. It demonstrates database creation, queries, and Angular integration using best practices.
Beyond simple CRUD and local data storage, RxDB supports:
-
-
-
Replication: Sync your local data with a remote database. Learn more at RxDB Replication.
-
-
-
Data Migration on Schema Changes: RxDB supports automatic or manual schema migrations to manage backward-compatibility and evolve your data structure. See RxDB Migration.
-
-
-
Encryption: Easily encrypt sensitive data at rest. See RxDB Encryption.
-
-
-
Compression: Reduce storage and bandwidth usage using key compression. Learn more at RxDB Key Compression.
Continue your deep dive into RxDB with official quickstart guides and star the repository on GitHub to stay updated.
-
-
-
RxDB Quickstart: Get started quickly with the RxDB Quickstart.
-
-
-
RxDB GitHub: Explore the source, open issues, and star ⭐ the project at RxDB GitHub Repo.
-
-
-
By combining IndexedDB's local storage with RxDB's powerful features, you can build performant, robust, and offline-capable Angular applications. RxDB takes care of the lower-level complexities, letting you focus on delivering a great user experience-online or off.
-
-
\ No newline at end of file
diff --git a/docs/articles/angular-indexeddb.md b/docs/articles/angular-indexeddb.md
deleted file mode 100644
index c2d58889f2b..00000000000
--- a/docs/articles/angular-indexeddb.md
+++ /dev/null
@@ -1,310 +0,0 @@
-# Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone
-
-> Discover how to harness IndexedDB in Angular with RxDB for robust offline apps. Learn reactive queries, advanced features, and more.
-
-import {Tabs} from '@site/src/components/tabs';
-import {Steps} from '@site/src/components/steps';
-
-# Build Smarter Offline-First Angular Apps: How RxDB Beats IndexedDB Alone
-
-In modern web applications, offline capabilities and fast interactions are crucial. IndexedDB, the [browser](./browser-database.md)'s built-in database, allows you to store data locally, making your Angular application more robust and responsive. However, IndexedDB can be cumbersome to work with directly. That's where RxDB (Reactive Database) shines. In this article, we'll walk you through how to utilize IndexedDB in your Angular project using [RxDB](https://rxdb.info/) as a convenient abstraction layer.
-
-## What Is IndexedDB?
-[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) is a low-level JavaScript API for client-side storage of large amounts of structured data. It allows you to create key-value or object store-based data storage right in the user's browser. IndexedDB supports transactions and indexing but lacks a robust query API and can be complex to use due to its callback-based nature.
-
-
-
-
-
-## Why Use IndexedDB in Angular
-
-- [Offline-First](../offline-first.md)/[Local-First](./local-first-future.md): If your app needs to function with limited or no internet connectivity, IndexedDB provides a reliable local storage layer. Users can continue using the application offline, and data can sync when the connection is restored.
-
-- **Performance**: Local data access comes with [near-zero latency](./zero-latency-local-first.md), removing the need for constant server requests and eliminating most loading spinners.
-
-- **Easier to Implement**: By replicating all necessary data to the client once, you avoid implementing numerous backend endpoints for each user interaction.
-
-- **Scalability**: Local data queries remove processing load from your servers and reduce bandwidth usage by handling queries on the client side.
-
-## Why Using Plain IndexedDB is a Problem
-
-Despite the advantages, directly working with IndexedDB has several drawbacks:
-
-- **Callback-Based**: IndexedDB was originally designed around a callback-based API, which can be unwieldy compared to modern Promise or RxJS-based flows.
-
-- **Difficult to Implement**: IndexedDB is often described as a "low-level" API. It's more suitable for library authors rather than application developers who simply need a robust local store.
-
-- **Rudimentary Query API**: Complex or dynamic queries are cumbersome with IndexedDB's basic get/put approach and limited indexes.
-
-- **TypeScript Support**: Maintaining strong TypeScript types for all document structures is not straightforward with IndexedDB's untyped object stores.
-
-- **No Observable API**: IndexedDB cannot directly emit live data changes. With RxDB, you can subscribe to changes on a collection or even a single document field.
-
-- **Cross-Tab Synchronization**: Handling concurrent data changes across multiple browser tabs is difficult in IndexedDB. RxDB has built-in multi-tab support that keeps all tabs in sync.
-
-- **Advanced Features Missing**: IndexedDB lacks built-in support for encryption, compression, or other advanced data management features.
-
-- **Browser-Only**: IndexedDB works in the browser but not in environments like React Native or Electron. RxDB offers storage adapters to seamlessly reuse the same code on different platforms.
-
-
-
-
-
-
-
-## Set Up RxDB in Angular
-
-### Installing RxDB
-
-You can [install RxDB](../install.md) into your Angular application via npm:
-
-```bash
-npm install rxdb --save
-```
-
-### Patch Change Detection with zone.js
-
-RxDB creates RxJS observables outside of Angular's zone, meaning Angular won't automatically trigger change detection when new data arrives. You must patch RxJS with zone.js:
-
-```ts
-//> app.component.ts
-/**
- * IMPORTANT: RxDB creates rxjs observables outside of Angular's zone
- * So you have to import the rxjs patch to ensure change detection works correctly.
- * @link https://www.bennadel.com/blog/3448-binding-rxjs-observable-sources-outside-of-the-ngzone-in-angular-6-0-2.htm
- */
-import 'zone.js/plugins/zone-patch-rxjs';
-```
-
-### Create a Database and Collections
-
-RxDB supports multiple storage options. The free and simple approach is using the [localstorage-based](../rx-storage-localstorage.md) storage. For higher performance, there's a premium plain [IndexedDB storage](../rx-storage-indexeddb.md).
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-
-// Define your schema
-const heroSchema = {
- title: 'hero schema',
- version: 0,
- description: 'Describes a hero in your app',
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- name: {
- type: 'string'
- },
- power: {
- type: 'string'
- }
- },
- required: ['id', 'name']
-};
-```
-
-
-
-### Localstorage
-
-```ts
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-export async function initDB() {
- // Create a database
- const db = await createRxDatabase({
- name: 'heroesdb', // the name of the database
- storage: getRxStorageLocalstorage()
- });
-
- // Add collections
- await db.addCollections({
- heroes: {
- schema: heroSchema
- }
- });
-
- return db;
-}
-```
-
-### IndexedDB
-
-```ts
-import { getRxStorageIndexedDB } from 'rxdb-premium/plugins/storage-indexeddb';
-export async function initDB() {
- // Create a database
- const db = await createRxDatabase({
- name: 'heroesdb', // the name of the database
- storage: getRxStorageIndexedDB()
- });
-
- // Add collections
- await db.addCollections({
- heroes: {
- schema: heroSchema
- }
- });
-
- return db;
-}
-```
-
-
-
-It's recommended to encapsulate database creation logic in an Angular service, such as in a DatabaseService. A full example is available in [RxDB's Angular example](https://github.com/pubkey/rxdb/blob/master/examples/angular/src/app/services/database.service.ts).
-
-### CRUD Operations
-
-Once your database is initialized, you can perform all CRUD operations:
-
-```ts
-// insert
-await db.heroes.insert({ name: 'Iron Man', power: 'Genius-level intellect' });
-
-// bulk insert
-await db.heroes.bulkInsert([
- { name: 'Thor', power: 'God of Thunder' },
- { name: 'Hulk', power: 'Superhuman Strength' }
-]);
-
-// find and findOne
-const heroes = await db.heroes.find().exec();
-const ironMan = await db.heroes.findOne({ selector: { name: 'Iron Man' } }).exec();
-
-// update
-const doc = await db.heroes.findOne({ selector: { name: 'Hulk' } }).exec();
-await doc.update({ $set: { power: 'Unlimited Strength' } });
-
-// delete
-const doc = await db.heroes.findOne({ selector: { name: 'Thor' } }).exec();
-await doc.remove();
-```
-
-## Reactive Queries and Live Updates
-
-A key benefit of RxDB is reactivity. You can subscribe to changes and have your UI automatically reflect updates in [real time](./realtime-database.md) even across browser tabs.
-
-
-
-### With RxJS Observables and Async Pipes
-
-In Angular, you can display this data with the `AsyncPipe`:
-
-```ts
-constructor(private dbService: DatabaseService) {
- this.heroes$ = this.dbService.db.heroes.find({
- selector: {},
- sort: [{ name: 'asc' }]
- }).$;
-}
-```
-
-```html
-
-
- {{ hero.name }}
-
-
-```
-
-### With Angular Signals
-
-Angular Signals are a newer approach for reactivity. RxDB supports them via a [custom reactivity](../reactivity.md) factory. You can convert RxJS Observables to Signals using Angular's `toSignal`:
-
-```ts
-import { RxReactivityFactory } from 'rxdb/plugins/core';
-import { Signal, untracked, Injector } from '@angular/core';
-import { toSignal } from '@angular/core/rxjs-interop';
-
-export function createReactivityFactory(injector: Injector): RxReactivityFactory> {
- return {
- fromObservable(observable$, initialValue) {
- return untracked(() =>
- toSignal(observable$, {
- initialValue,
- injector,
- rejectErrors: true
- })
- );
- }
- };
-}
-```
-
-Pass this factory when creating your [RxDatabase](../rx-database.md):
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { inject, Injector } from '@angular/core';
-
-const database = await createRxDatabase({
- name: 'mydb',
- storage: getRxStorageLocalstorage(),
- reactivity: createReactivityFactory(inject(Injector))
-});
-```
-
-Use the double-dollar sign (`$$`) to get a `Signal` instead of an `Observable`:
-
-```ts
-const heroesSignal = database.heroes.find().$$;
-```
-
-```html
-
-
- {{ hero.name }}
-
-
-```
-
-## Angular IndexedDB Example with RxDB
-
-A comprehensive example of RxDB in an Angular application is available in the [RxDB GitHub repository](https://github.com/pubkey/rxdb/tree/master/examples/angular). It demonstrates [database](./angular-database.md) creation, queries, and Angular integration using best practices.
-
-## Advanced RxDB Features
-
-Beyond simple CRUD and local data storage, RxDB supports:
-
-- **Replication**: Sync your local data with a remote database. Learn more at [RxDB Replication](https://rxdb.info/replication.html).
-
-- **Data Migration on Schema Changes**: RxDB supports automatic or manual schema migrations to manage backward-compatibility and evolve your data structure. See [RxDB Migration](https://rxdb.info/migration-schema.html).
-
-- **Encryption**: Easily encrypt sensitive data at rest. See [RxDB Encryption](https://rxdb.info/encryption.html).
-
-- **Compression**: Reduce storage and bandwidth usage using key compression. Learn more at [RxDB Key Compression](https://rxdb.info/key-compression.html).
-
-## Limitations of IndexedDB
-
-While IndexedDB works well for many use cases, it does have a few constraints:
-
-- **Potentially Slow**: While adequate for most use cases, IndexedDB performance can degrade for very large datasets. More details at RxDB [Slow IndexedDB](../slow-indexeddb.md).
-
-- **Storage Limits**: Browsers may cap the amount of data you can store in IndexedDB. For more info, see [Local Storage Limits of IndexedDB](./indexeddb-max-storage-limit.md).
-
-## Alternatives to IndexedDB
-
-Depending on your needs, you might explore:
-
-- **Origin Private File System (OPFS)**: A newer browser storage mechanism that can offer better performance. RxDB supports [OPFS storage](../rx-storage-opfs.md).
-
-- **SQLite**: When building a mobile or hybrid app (e.g., with [Capacitor](../capacitor-database.md) or [Ionic](./ionic-database.md)), you can use SQLite locally. See [RxDB with SQLite](../rx-storage-sqlite.md).
-
-## Performance comparison with other browser storages
-Here is a [performance overview](../rx-storage-performance.md) of the various browser based storage implementation of RxDB:
-
-
-
-## Follow Up
-
-Continue your deep dive into RxDB with official quickstart guides and star the repository on GitHub to stay updated.
-
-- **RxDB Quickstart**: Get started quickly with the [RxDB Quickstart](../quickstart.md).
-
-- **RxDB GitHub**: Explore the source, open issues, and star ⭐ the project at [RxDB GitHub Repo](https://github.com/pubkey/rxdb).
-
-By combining IndexedDB's local storage with RxDB's powerful features, you can build performant, robust, and offline-capable Angular applications. RxDB takes care of the lower-level complexities, letting you focus on delivering a great user experience-online or off.
diff --git a/docs/articles/browser-database.html b/docs/articles/browser-database.html
deleted file mode 100644
index 5dad75ae1d4..00000000000
--- a/docs/articles/browser-database.html
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-Benefits of RxDB & Browser Databases | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In the world of web development, efficient data management is a cornerstone of building successful and performant applications. The ability to store data directly in the browser brings numerous advantages, such as caching, offline accessibility, simplified replication of database state, and real-time application development. In this article, we will explore RxDB, a powerful browser JavaScript database, and understand why it is an excellent choice for implementing a browser database solution.
By leveraging a browser database, you can harness the power of caching. Storing frequently accessed data locally enables you to reduce server requests and greatly improve application performance. Caching provides a faster and smoother user experience, enhancing overall user satisfaction.
Storing data in the browser allows for offline accessibility. Regardless of an active internet connection, users can access and interact with the application, ensuring uninterrupted productivity and user engagement.
-
Easier implementation of replicating database state
-
Browser databases simplify the replication of database state across multiple devices or instances of the application. Compared to complex REST routes, replicating data becomes easier and more streamlined. This capability enables the development of real-time and collaborative applications, where changes are seamlessly synchronized among users.
-
Building real-time applications is easier with local data
-
With a local browser database, building real-time applications becomes more straightforward. The availability of local data allows for reactive data flows and dynamic user interfaces that instantly reflect changes in the underlying data. Real-time features can be seamlessly implemented, providing a rich and interactive user experience.
Browser databases distribute the query workload to users' devices, allowing queries to run locally instead of relying solely on server resources. This decentralized approach improves scalability by reducing the burden on the server, resulting in a more efficient and responsive application.
Browser databases offer the advantage of running queries locally, resulting in low latency. Eliminating the need for server round-trips significantly improves query performance, ensuring faster data retrieval and a more responsive application.
Storing data in the browser reduces the initial application start time. Instead of waiting for data to be fetched from the server, the application can leverage the local database, resulting in faster initialization and improved user satisfaction right from the start.
Browser databases, including RxDB, seamlessly integrate with popular JavaScript frameworks such as Angular, React.js, Vue.js, and Svelte. This integration allows developers to leverage the power of a database while working within the familiar environment of their preferred framework, enhancing productivity and ease of development.
Security is a crucial aspect of data storage, especially when handling sensitive information. Browser databases, like RxDB, offer the capability to store local data with encryption, ensuring the confidentiality and protection of sensitive user data.
Utilizing a local browser database for state management eliminates the need for traditional state management libraries like Redux or NgRx. This approach simplifies the application's architecture by leveraging the database's capabilities to handle state-related operations efficiently.
-
Data is portable and always accessible by the user
-
When data is stored in the browser, it becomes portable and always accessible by the user. This ensures that users have control and ownership of their data, enhancing data privacy and accessibility.
-
Why SQL databases like SQLite are not a good fit for the browser
-
While SQL databases, such as SQLite, excel in server-side scenarios, they are not always the optimal choice for browser-based applications. Here are some reasons why SQL databases may not be the best fit for the browser:
SQL databases typically rely on a push/pull mechanism, where the server pushes updates to the client or the client pulls data from the server. This approach is not inherently reactive and requires additional effort to implement real-time data updates. In contrast, browser databases like RxDB provide built-in reactive mechanisms, allowing the application to react to data changes seamlessly.
Server-side databases, designed to handle large-scale applications, often have significant build sizes that are unsuitable for browser applications. In contrast, browser databases are specifically optimized for browser environments and leverage browser APIs like IndexedDB, OPFS, and Webworker, resulting in smaller build sizes.
The initialization time and performance of server-side databases can be suboptimal in browser applications. Browser databases, on the other hand, are designed to provide fast initialization and efficient performance within the browser environment, ensuring a smooth user experience.
RxDB stands out as an excellent choice for implementing a browser database solution. Here's why RxDB is a perfect fit for browser applications:
-
Observable Queries (rxjs) to automatically update the UI on changes
-
RxDB provides Observable Queries, powered by RxJS, enabling automatic UI updates when data changes occur. This reactive approach eliminates the need for manual data synchronization and ensures a real-time and responsive user interface.
RxDB utilizes NoSQL JSON documents, which align naturally with UI development in JavaScript. JavaScript's native handling of JSON objects makes working with NoSQL documents more intuitive, simplifying UI-related operations.
-
NoSQL has better TypeScript support compared to SQL
-
TypeScript is widely used in modern JavaScript development. NoSQL databases, including RxDB, offer excellent TypeScript support, making it easier to build type-safe applications and leverage the benefits of static typing.
RxDB allows observing individual document fields, providing granular reactivity. This feature enables efficient tracking of specific data changes and fine-grained UI updates, optimizing performance and responsiveness.
-
Made in JavaScript, optimized for JavaScript applications
-
RxDB is built entirely in JavaScript, optimized for JavaScript applications. This ensures seamless integration with JavaScript codebases and maximizes performance within the browser environment.
-
Optimized observed queries with the EventReduce Algorithm
-
RxDB employs the EventReduce Algorithm to optimize observed queries. This algorithm intelligently reduces unnecessary data transmissions, resulting in efficient query execution and improved performance.
RxDB natively supports multi-tab applications, allowing data synchronization and replication across different tabs or instances of the same application. This feature ensures consistent data across the application and enhances collaboration and real-time experiences.
RxDB excels in handling schema changes, even when data is stored on multiple client devices. It provides mechanisms to handle schema migrations seamlessly, ensuring data integrity and compatibility as the application evolves.
To optimize storage space, RxDB allows the compression of documents. Storing compressed documents reduces storage requirements and improves overall performance, especially in scenarios with large data volumes.
RxDB offers a flexible storage layer, enabling code reuse across different platforms, including Electron.js, React Native, hybrid apps (e.g., Capacitor.js), and web browsers. This flexibility streamlines development efforts and ensures consistent data management across multiple platforms.
-
Replication Algorithm for compatibility with any backend
-
RxDB incorporates a Replication Algorithm that is open-source and can be made compatible with various backend systems. This compatibility allows seamless data synchronization with different backend architectures, such as own servers, Firebase, CouchDB, NATS or WebSocket.
To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-
-
RxDB empowers developers to unlock the power of browser databases, enabling efficient data management, real-time applications, and enhanced user experiences. By leveraging RxDB's features and benefits, you can take your browser-based applications to the next level of performance, scalability, and responsiveness.
-
-
\ No newline at end of file
diff --git a/docs/articles/browser-database.md b/docs/articles/browser-database.md
deleted file mode 100644
index a2cd370ea77..00000000000
--- a/docs/articles/browser-database.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# Benefits of RxDB & Browser Databases
-
-> Find out why RxDB is the go-to solution for browser databases. See how it boosts performance, simplifies replication, and powers real-time UIs.
-
-# RxDB: The benefits of Browser Databases
-In the world of web development, efficient data management is a cornerstone of building successful and performant applications. The ability to store data directly in the browser brings numerous advantages, such as caching, offline accessibility, simplified replication of database state, and real-time application development. In this article, we will explore [RxDB](https://rxdb.info/), a powerful browser JavaScript database, and understand why it is an excellent choice for implementing a browser database solution.
-
-
-
-
-
-
-
-## Why you might want to store data in the browser
-There are compelling reasons to consider storing data in the browser:
-
-### Use the database for caching
-By leveraging a browser database, you can harness the power of caching. Storing frequently accessed data locally enables you to reduce server requests and greatly improve application performance. Caching provides a faster and smoother user experience, enhancing overall user satisfaction.
-
-### Data is offline accessible
-Storing data in the browser allows for offline accessibility. Regardless of an active internet connection, users can access and interact with the application, ensuring uninterrupted productivity and user engagement.
-
-### Easier implementation of replicating database state
-Browser databases simplify the replication of database state across multiple devices or instances of the application. Compared to complex REST routes, replicating data becomes easier and more streamlined. This capability enables the development of real-time and collaborative applications, where changes are seamlessly synchronized among users.
-
-### Building real-time applications is easier with local data
-With a local browser database, building real-time applications becomes more straightforward. The availability of local data allows for reactive data flows and dynamic user interfaces that instantly reflect changes in the underlying data. Real-time features can be seamlessly implemented, providing a rich and interactive user experience.
-
-### Browser databases can scale better
-Browser databases distribute the query workload to users' devices, allowing queries to run locally instead of relying solely on server resources. This decentralized approach improves scalability by reducing the burden on the server, resulting in a more efficient and responsive application.
-
-### Running queries locally has low latency
-Browser databases offer the advantage of running queries locally, resulting in low latency. Eliminating the need for server round-trips significantly improves query performance, ensuring faster data retrieval and a more responsive application.
-
-### Faster initial application start time
-Storing data in the browser reduces the initial application start time. Instead of waiting for data to be fetched from the server, the application can leverage the [local database](./local-database.md), resulting in faster initialization and improved user satisfaction right from the start.
-
-### Easier integration with JavaScript frameworks
-Browser databases, including [RxDB](https://rxdb.info/), seamlessly integrate with popular JavaScript frameworks such as [Angular](./angular-database.md), [React.js](./react-database.md), [Vue.js](./vue-database.md), and Svelte. This integration allows developers to leverage the power of a database while working within the familiar environment of their preferred framework, enhancing productivity and ease of development.
-
-### Store local data with encryption
-Security is a crucial aspect of data storage, especially when handling sensitive information. Browser databases, like RxDB, offer the capability to store local data with encryption, ensuring the confidentiality and protection of sensitive user data.
-
-### Using a local database for state management
-Utilizing a local browser database for state management eliminates the need for traditional state management libraries like Redux or NgRx. This approach simplifies the application's architecture by leveraging the database's capabilities to handle state-related operations efficiently.
-
-### Data is portable and always accessible by the user
-When data is stored in the browser, it becomes portable and always accessible by the user. This ensures that users have control and ownership of their data, enhancing data privacy and accessibility.
-
-## Why SQL databases like SQLite are not a good fit for the browser
-While SQL databases, such as SQLite, excel in server-side scenarios, they are not always the optimal choice for browser-based applications. Here are some reasons why SQL databases may not be the best fit for the browser:
-
-### Push/Pull based vs. reactive
-SQL databases typically rely on a push/pull mechanism, where the server pushes updates to the client or the client pulls data from the server. This approach is not inherently reactive and requires additional effort to implement real-time data updates. In contrast, browser databases like [RxDB](https://rxdb.info/) provide built-in reactive mechanisms, allowing the application to react to data changes seamlessly.
-
-### Build size of server-side databases
-Server-side databases, designed to handle large-scale applications, often have significant build sizes that are unsuitable for browser applications. In contrast, browser databases are specifically optimized for browser environments and leverage browser APIs like [IndexedDB](../rx-storage-indexeddb.md), [OPFS](../rx-storage-opfs.md), and [Webworker](../rx-storage-worker.md), resulting in smaller build sizes.
-
-### Initialization time and performance
-The initialization time and performance of server-side databases can be suboptimal in browser applications. Browser databases, on the other hand, are designed to provide fast initialization and efficient performance within the browser environment, ensuring a smooth user experience.
-
-## Why RxDB is a good fit for the browser
-RxDB stands out as an excellent choice for implementing a browser database solution. Here's why RxDB is a perfect fit for browser applications:
-
-### Observable Queries (rxjs) to automatically update the UI on changes
-RxDB provides Observable Queries, powered by RxJS, enabling automatic UI updates when data changes occur. This reactive approach eliminates the need for manual data synchronization and ensures a real-time and responsive user interface.
-
-```typescript
-const query = myCollection.find({
- selector: {
- age: {
- $gt: 21
- }
- }
-});
-const querySub = query.$.subscribe(results => {
- console.log('got results: ' + results.length);
-});
-```
-
-### NoSQL [JSON](./json-database.md) documents are a better fit for UIs
-RxDB utilizes NoSQL [JSON documents](./json-database.md), which align naturally with UI development in JavaScript. JavaScript's native handling of JSON objects makes working with NoSQL documents more intuitive, simplifying UI-related operations.
-
-### NoSQL has better TypeScript support compared to SQL
-TypeScript is widely used in modern JavaScript development. [NoSQL databases](./in-memory-nosql-database.md), including RxDB, offer excellent TypeScript support, making it easier to build type-safe applications and leverage the benefits of static typing.
-
-### Observable document fields
-RxDB allows observing individual document fields, providing granular reactivity. This feature enables efficient tracking of specific data changes and fine-grained UI updates, optimizing performance and responsiveness.
-
-### Made in JavaScript, optimized for JavaScript applications
-RxDB is built entirely in JavaScript, optimized for JavaScript applications. This ensures seamless integration with JavaScript codebases and maximizes performance within the browser environment.
-
-### Optimized observed queries with the EventReduce Algorithm
-RxDB employs the EventReduce Algorithm to optimize observed queries. This algorithm intelligently reduces unnecessary data transmissions, resulting in efficient query execution and improved performance.
-
-### Built-in multi-tab support
-RxDB natively supports multi-tab applications, allowing data synchronization and replication across different tabs or instances of the same application. This feature ensures consistent data across the application and enhances collaboration and real-time experiences.
-
-
-
-### Handling of schema changes
-RxDB excels in handling schema changes, even when data is stored on multiple client devices. It provides mechanisms to handle schema migrations seamlessly, ensuring data integrity and compatibility as the application evolves.
-
-### Storing documents compressed
-To optimize [storage](./browser-storage.md) space, RxDB allows the [compression](../key-compression.md) of documents. Storing compressed documents reduces storage requirements and improves overall performance, especially in scenarios with large data volumes.
-
-### Flexible storage layer for various platforms
-RxDB offers a flexible storage layer, enabling code reuse across different platforms, including [Electron.js](../electron-database.md), React Native, hybrid apps (e.g., Capacitor.js), and web browsers. This flexibility streamlines development efforts and ensures consistent data management across multiple platforms.
-
-### Replication Algorithm for compatibility with any backend
-RxDB incorporates a [Replication Algorithm](../replication.md) that is open-source and can be made compatible with various backend systems. This compatibility allows seamless data synchronization with different backend architectures, such as own servers, [Firebase](../replication-firestore.md), [CouchDB](../replication-couchdb.md), [NATS](../replication-nats.md) or [WebSocket](../replication-websocket.md).
-
-
-
-## Follow Up
-To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-
-[RxDB](https://rxdb.info/) empowers developers to unlock the power of browser databases, enabling efficient data management, real-time applications, and enhanced user experiences. By leveraging RxDB's features and benefits, you can take your browser-based applications to the next level of performance, scalability, and responsiveness.
diff --git a/docs/articles/browser-storage.html b/docs/articles/browser-storage.html
deleted file mode 100644
index 8bf1f043583..00000000000
--- a/docs/articles/browser-storage.html
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
-
-
-Browser Storage - RxDB as a Database for Browsers | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
When it comes to building web applications, one essential aspect is the storage of data. Two common methods of storing data directly within the user's web browser are Localstorage and IndexedDB. These browser-based storage options serve various purposes and cater to different needs in web development.
Localstorage is a straightforward way to store small amounts of data in the user's web browser. It operates on a simple key-value basis and is relatively easy to use. While it has limitations, it is suitable for basic data storage requirements.
IndexedDB, on the other hand, offers a more robust and structured approach to browser-based data storage. It can handle larger datasets and complex queries, making it a valuable choice for more advanced web applications.
Now that we've explored the methods of storing data in the browser, let's delve into why this is a beneficial strategy for web developers:
-
-
-
Caching:
-Storing data in the browser allows you to cache frequently used information. This means that your web application can access essential data more quickly because it doesn't need to repeatedly fetch it from a server. This results in a smoother and more responsive user experience.
-
-
-
Offline Access:
-One significant advantage of browser storage is that data becomes portable and remains accessible even when the user is offline. This feature ensures that users can continue to use your application, view their saved information, and make changes, irrespective of their internet connection status.
-
-
-
Faster Real-time Applications:
-For real-time applications, having data stored locally in the browser significantly enhances performance. Local data allows your application to respond faster to user interactions, creating a more seamless and responsive user interface.
-
-
-
Low Latency Queries:
-When you run queries locally within the browser, you minimize the latency associated with network requests. This results in near-instant access to data, which is particularly crucial for applications that require rapid data retrieval.
-
-
-
Faster Initial Application Start Time:
-By preloading essential data into browser storage, you can reduce the initial load time of your web application. Users can start using your application more swiftly, which is essential for making a positive first impression.
-
-
-
Store Local Data with Encryption:
-For applications that deal with sensitive data, browser storage allows you to implement encryption to secure the stored information. This ensures that even if data is stored on the user's device, it remains confidential and protected.
-
-
-
In summary, storing data in the browser offers several advantages, including improved performance, offline access, and enhanced user experiences. Localstorage and IndexedDB are two valuable tools that developers can utilize to leverage these benefits and create web applications that are more responsive and user-friendly.
While browser storage, such as Localstorage and IndexedDB, offers many advantages, it's important to be aware of its limitations:
-
-
-
Slower Performance Compared to Native Databases: Browser-based storage solutions can't match the performance of native server-side databases. They may experience slower data retrieval and processing, especially for large datasets or complex operations.
-
-
-
Storage Space Limitations: Browsers impose restrictions on the amount of data that can be stored locally. This limitation can be problematic for applications with extensive data storage requirements, potentially necessitating creative solutions to manage data effectively.
-
-
-
Why SQL Databases Like SQLite Aren't a Good Fit for the Browser
-
SQL databases like SQLite, while powerful in server environments, may not be the best choice for browser-based applications due to various reasons:
SQL databases often use a push/pull model for data synchronization. This approach is less reactive and may not align well with the real-time nature of web applications, where immediate updates to the user interface are crucial.
Server-side databases like SQLite have a significant build size, which can increase the initial load time of web applications. This can result in a suboptimal user experience, particularly for users with slower internet connections.
SQL databases are optimized for server environments, and their initialization processes and performance characteristics may not align with the needs of web applications. They might not offer the swift performance required for seamless user interactions.
RxDB offers a flexible storage layer that can seamlessly integrate with different platforms, making it versatile and adaptable to various application needs.
NoSQL JSON documents, used by RxDB, are well-suited for user interfaces. They provide a natural and efficient way to structure and display data in web applications.
-
NoSQL Has Better TypeScript Support Compared to SQL
-
RxDB boasts robust TypeScript support, which is beneficial for developers who prefer type safety and code predictability in their projects.
RxDB enables developers to observe individual document fields, offering fine-grained control over data tracking and updates.
-
Made in JavaScript, Optimized for JavaScript Applications
-
Being built in JavaScript and optimized for JavaScript applications, RxDB seamlessly integrates into web development stacks, minimizing compatibility issues.
-
Observable Queries (rxjs) to Automatically Update the UI on Changes
-
RxDB's support for Observable Queries allows the user interface to update automatically in real-time when data changes. This reactivity enhances the user experience and simplifies UI development.
Efficient data storage is achieved through document compression, reducing storage space requirements and enhancing overall performance.
-
Replication Algorithm for Compatibility with Any Backend
-
RxDB's Replication Algorithm facilitates compatibility with various backend systems, ensuring seamless data synchronization between the browser and server.
In conclusion, RxDB is a powerful and feature-rich solution for browser-based storage. Its adaptability, real-time capabilities, TypeScript support, and optimization for JavaScript applications make it an ideal choice for modern web development projects, addressing the limitations of traditional SQL databases in the browser. Developers can harness RxDB to create efficient, responsive, and user-friendly web applications that leverage the full potential of browser storage.
To explore more about RxDB and leverage its capabilities for browser storage, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-
-
\ No newline at end of file
diff --git a/docs/articles/browser-storage.md b/docs/articles/browser-storage.md
deleted file mode 100644
index db698dbb89d..00000000000
--- a/docs/articles/browser-storage.md
+++ /dev/null
@@ -1,132 +0,0 @@
-# Browser Storage - RxDB as a Database for Browsers
-
-> Explore RxDB for browser storage its advantages, limitations, and why it outperforms SQL databases in web applications for enhanced efficiency
-
-# Browser Storage - RxDB as a Database for Browsers
-
-**Storing Data in the Browser**
-
-When it comes to building web applications, one essential aspect is the storage of data. Two common methods of storing data directly within the user's web browser are Localstorage and [IndexedDB](../rx-storage-indexeddb.md). These browser-based storage options serve various purposes and cater to different needs in web development.
-
-
-
-
-
-
-
-### Localstorage
-[Localstorage](./localstorage.md) is a straightforward way to store small amounts of data in the user's web browser. It operates on a simple key-value basis and is relatively easy to use. While it has limitations, it is suitable for basic data storage requirements.
-
-### IndexedDB
-IndexedDB, on the other hand, offers a more robust and structured approach to browser-based data storage. It can handle larger datasets and complex queries, making it a valuable choice for more advanced web applications.
-
-## Why Store Data in the Browser
-Now that we've explored the methods of storing data in the browser, let's delve into why this is a beneficial strategy for web developers:
-
-1. **Caching**:
-Storing data in the browser allows you to cache frequently used information. This means that your web application can access essential data more quickly because it doesn't need to repeatedly fetch it from a server. This results in a smoother and more responsive user experience.
-
-2. **Offline Access**:
-One significant advantage of browser storage is that data becomes portable and remains accessible even when the user is offline. This feature ensures that users can continue to use your application, view their saved information, and make changes, irrespective of their internet connection status.
-
-3. **Faster Real-time Applications**:
-For real-time applications, having data stored locally in the browser significantly enhances performance. Local data allows your application to respond faster to user interactions, creating a more seamless and responsive user interface.
-
-4. **Low Latency Queries**:
-When you run queries locally within the browser, you minimize the latency associated with network requests. This results in near-instant access to data, which is particularly crucial for applications that require rapid data retrieval.
-
-5. **Faster Initial Application Start Time**:
-By preloading essential data into browser storage, you can reduce the initial load time of your web application. Users can start using your application more swiftly, which is essential for making a positive first impression.
-
-6. **Store Local Data with Encryption**:
-For applications that deal with sensitive data, browser storage allows you to implement [encryption](../encryption.md) to secure the stored information. This ensures that even if data is stored on the user's device, it remains confidential and protected.
-
-In summary, storing data in the browser offers several advantages, including improved performance, offline access, and enhanced user experiences. Localstorage and IndexedDB are two valuable tools that developers can utilize to leverage these benefits and create web applications that are more responsive and user-friendly.
-
-## Browser Storage Limitations
-While browser storage, such as Localstorage and IndexedDB, offers many advantages, it's important to be aware of its limitations:
-
-- **Slower Performance Compared to Native Databases**: Browser-based storage solutions can't match the [performance](../rx-storage-performance.md) of native server-side databases. They may experience slower data retrieval and processing, especially for large datasets or complex operations.
-
-- **Storage Space Limitations**: Browsers [impose restrictions on the amount of data that can be stored locally](./indexeddb-max-storage-limit.md). This limitation can be problematic for applications with extensive data storage requirements, potentially necessitating creative solutions to manage data effectively.
-
-## Why SQL Databases Like SQLite Aren't a Good Fit for the Browser
-SQL databases like SQLite, while powerful in server environments, may not be the best choice for browser-based applications due to various reasons:
-
-### Push/Pull Based vs. Reactive
-SQL databases often use a push/pull model for data synchronization. This approach is less reactive and may not align well with the real-time nature of web applications, where immediate updates to the user interface are crucial.
-
-### Build Size of Server-Side Databases
-Server-side databases like SQLite have a significant build size, which can increase the initial load time of web applications. This can result in a suboptimal user experience, particularly for users with slower internet connections.
-
-### Initialization Time and Performance
-SQL databases are optimized for server environments, and their initialization processes and performance characteristics may not align with the needs of web applications. They might not offer the swift performance required for seamless user interactions.
-
-## Why RxDB Is a Good Fit as Browser Storage
-RxDB is an excellent choice for browser-based storage due to its numerous features and advantages:
-
-
-
-
-
-
-
-### Flexible Storage Layer for Various Platforms
-RxDB offers a flexible storage layer that can seamlessly integrate with different platforms, making it versatile and adaptable to various application needs.
-
-### NoSQL JSON Documents Are a Better Fit for UIs
-NoSQL [JSON documents](./json-database.md), used by [RxDB](https://rxdb.info/), are well-suited for user interfaces. They provide a natural and efficient way to structure and display data in web applications.
-
-### NoSQL Has Better TypeScript Support Compared to SQL
-RxDB boasts robust TypeScript support, which is beneficial for developers who prefer type safety and code predictability in their projects.
-
-### Observable Document Fields
-RxDB enables developers to observe individual document fields, offering fine-grained control over data tracking and updates.
-
-### Made in JavaScript, Optimized for JavaScript Applications
-Being built in JavaScript and optimized for JavaScript applications, RxDB seamlessly integrates into web development stacks, minimizing compatibility issues.
-
-### Observable Queries (rxjs) to Automatically Update the UI on Changes
-RxDB's support for Observable Queries allows the user interface to update automatically in real-time when data changes. This reactivity enhances the user experience and simplifies UI development.
-
-```typescript
-const query = myCollection.find({
- selector: {
- age: {
- $gt: 21
- }
- }
-});
-const querySub = query.$.subscribe(results => {
- console.log('got results: ' + results.length);
-});
-```
-
-### Optimized Observed Queries with the EventReduce Algorithm
-RxDB's [EventReduce Algorithm](https://github.com/pubkey/event-reduce) ensures efficient data handling and rendering, improving overall performance and responsiveness.
-
-### Handling of Schema Changes
-RxDB provides built-in support for [handling schema changes](../migration-schema.md), simplifying database management when updates are required.
-
-### Built-In Multi-Tab Support
-For applications requiring multi-tab support, RxDB natively handles data consistency across different browser tabs, streamlining data synchronization.
-
-
-
-### Storing Documents Compressed
-Efficient data storage is achieved through [document compression](../key-compression.md), reducing storage space requirements and enhancing overall performance.
-
-### Replication Algorithm for Compatibility with Any Backend
-RxDB's [Replication Algorithm](../replication.md) facilitates compatibility with various backend systems, ensuring seamless data synchronization between the browser and server.
-
-
-
-## Summary
-
-In conclusion, RxDB is a powerful and feature-rich solution for browser-based storage. Its adaptability, real-time capabilities, TypeScript support, and optimization for JavaScript applications make it an ideal choice for modern web development projects, addressing the limitations of traditional SQL databases in the browser. Developers can harness RxDB to create efficient, responsive, and user-friendly web applications that leverage the full potential of browser storage.
-
-## Follow Up
-To explore more about RxDB and leverage its capabilities for browser storage, check out the following resources:
-
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
diff --git a/docs/articles/data-base.html b/docs/articles/data-base.html
deleted file mode 100644
index a041d50ac06..00000000000
--- a/docs/articles/data-base.html
+++ /dev/null
@@ -1,88 +0,0 @@
-
-
-
-
-
-Empower Web Apps with Reactive RxDB Data-base | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
RxDB as a data base: Empowering Web Applications with Reactive Data Handling
-
In the world of web applications, efficient data management plays a crucial role in delivering a seamless user experience. As mobile applications continue to dominate the digital landscape, the importance of robust data bases becomes evident. In this article, we will explore RxDB as a powerful data base solution for web applications. We will delve into its features, advantages, and advanced techniques, highlighting its ability to handle reactive data and enable an offline-first approach.
-
-
Overview of Web Applications that can benefit from RxDB
-
Before diving into the specifics of RxDB, let's take a moment to understand the scope of web applications that can leverage its capabilities. Any web application that requires real-time data updates, offline functionality, and synchronization between clients and servers can greatly benefit from RxDB. Whether it's a collaborative document editing tool, a task management app, or a chat application, RxDB offers a robust foundation for building these types of applications.
Mobile applications have become an integral part of our lives, providing us with instant access to information and services. Behind the scenes, data bases play a pivotal role in storing and managing the data that powers these applications. data bases enable efficient data retrieval, updates, and synchronization, ensuring a smooth user experience even in challenging network conditions.
RxDB, short for Reactive data base, is a client-side data base solution designed specifically for web and mobile applications. Built on the principles of reactive programming, RxDB brings the power of observables and event-driven architecture to data management. With RxDB, developers can create applications that are responsive, offline-ready, and capable of seamless data synchronization between clients and servers.
RxDB is an open-source JavaScript data base that leverages reactive programming and provides a seamless API for handling data. It is built on top of existing popular data base technologies, such as IndexedDB, and adds a layer of reactive features to enable real-time data updates and synchronization.
One of the standout features of RxDB is its reactive data handling. It utilizes observables to provide a stream of data that automatically updates whenever a change occurs. This reactive approach allows developers to build applications that respond instantly to data changes, ensuring a highly interactive and real-time user experience.
RxDB embraces an offline-first approach, enabling applications to work seamlessly even when there is no internet connectivity. It achieves this by caching data locally on the client-side and synchronizing it with the server when the connection is available. This ensures that users can continue working with the application and have their data automatically synchronized when they come back online.
RxDB simplifies the process of data replication between clients and servers. It provides replication plugins that handle the synchronization of data in real-time. These plugins allow applications to keep data consistent across multiple clients, enabling collaborative features and ensuring that each client has the most up-to-date information.
RxDB introduces the concept of observable queries, which are powerful tools for efficiently querying data. With observable queries, developers can subscribe to specific data queries and receive automatic updates whenever the underlying data changes. This eliminates the need for manual polling and ensures that applications always have access to the latest data.
RxDB offers multi-tab support, allowing applications to function seamlessly across multiple browser tabs. This feature ensures that data changes in one tab are immediately reflected in all other open tabs, enabling a consistent user experience across different browser windows.
When considering data base options for web applications, developers often encounter choices like IndexedDB, OPFS, and Memory-based solutions. RxDB, while built on top of IndexedDB, stands out due to its reactive data handling capabilities and advanced synchronization features. Compared to other options, RxDB offers a more streamlined and powerful approach to managing data in web applications.
IndexedDB RxStorage: This layer directly utilizes IndexedDB as its backend, providing a robust and widely supported storage option.
-
OPFS RxStorage: OPFS (Operational Transformation File System) is a file system-like storage layer that allows for efficient conflict resolution and real-time collaboration.
-
Memory RxStorage: Primarily used for testing and development, this storage layer keeps data in memory without persisting it to disk.
-Each RxStorage layer has its strengths and is suited for different scenarios, enabling developers to choose the most appropriate option for their specific use case.
-
-
Synchronizing Data with RxDB between Clients and Servers
As mentioned earlier, RxDB adopts an offline-first approach, allowing applications to function seamlessly in disconnected environments. By caching data locally, applications can continue to operate and make updates even without an internet connection. Once the connection is restored, RxDB's replication plugins take care of synchronizing the data with the server, ensuring consistency across all clients.
RxDB provides a range of replication plugins that simplify the process of synchronizing data between clients and servers. These plugins enable real-time replication using various protocols, such as WebSocket or HTTP, and handle conflict resolution strategies to ensure data integrity. By leveraging these replication plugins, developers can easily implement robust and scalable synchronization capabilities in their applications.
Indexing and Performance Optimization
-To achieve optimal performance, RxDB offers indexing capabilities. Indexing allows for efficient data retrieval and faster query execution. By strategically defining indexes on frequently accessed fields, developers can significantly enhance the overall performance of their RxDB-powered applications.
In scenarios where data security is paramount, RxDB provides options for encrypting local data. By encrypting the data base contents, developers can ensure that sensitive information remains secure even if the underlying storage is compromised. RxDB integrates seamlessly with encryption libraries, making it easy to implement end-to-end encryption in applications.
RxDB offers change streams and event handling mechanisms, enabling developers to react to data changes in real-time. With change streams, applications can listen to specific collections or documents and trigger custom logic whenever a change occurs. This capability opens up possibilities for building real-time collaboration features, notifications, or other reactive behaviors.
In scenarios where storage size is a concern, RxDB provides JSON key compression. By applying compression techniques to JSON keys, developers can significantly reduce the storage footprint of their data bases. This feature is particularly beneficial for applications dealing with large datasets or limited storage capacities.
RxDB provides an exceptional data base solution for web and mobile applications, empowering developers to create reactive, offline-ready, and synchronized applications. With its reactive data handling, offline-first approach, and replication plugins, RxDB simplifies the challenges of building real-time applications with data synchronization requirements. By embracing advanced features like indexing, encryption, change streams, and JSON key compression, developers can optimize performance, enhance security, and reduce storage requirements. As web and mobile applications continue to evolve, RxDB proves to be a reliable and powerful
-
-
\ No newline at end of file
diff --git a/docs/articles/data-base.md b/docs/articles/data-base.md
deleted file mode 100644
index ad538f20903..00000000000
--- a/docs/articles/data-base.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# Empower Web Apps with Reactive RxDB Data-base
-
-> Explore RxDB's reactive data-base solution for web and mobile. Enable offline-first experiences, real-time syncing, and secure data handling with ease.
-
-# RxDB as a data base: Empowering Web Applications with Reactive Data Handling
-In the world of web applications, efficient data management plays a crucial role in delivering a seamless user experience. As mobile applications continue to dominate the digital landscape, the importance of robust data bases becomes evident. In this article, we will explore RxDB as a powerful data base solution for web applications. We will delve into its features, advantages, and advanced techniques, highlighting its ability to handle reactive data and enable an offline-first approach.
-
-
-
-
-
-
-
-## Overview of Web Applications that can benefit from RxDB
-Before diving into the specifics of RxDB, let's take a moment to understand the scope of web applications that can leverage its capabilities. Any web application that requires real-time data updates, offline functionality, and synchronization between clients and servers can greatly benefit from RxDB. Whether it's a collaborative document editing tool, a task management app, or a chat application, RxDB offers a robust foundation for building these types of applications.
-
-## Importance of data bases in Mobile Applications
-Mobile applications have become an integral part of our lives, providing us with instant access to information and services. Behind the scenes, data bases play a pivotal role in storing and managing the data that powers these applications. data bases enable efficient data retrieval, updates, and synchronization, ensuring a smooth user experience even in challenging network conditions.
-
-## Introducing RxDB as a data base Solution
-RxDB, short for Reactive data base, is a client-side data base solution designed specifically for web and mobile applications. Built on the principles of reactive programming, RxDB brings the power of observables and event-driven architecture to data management. With RxDB, developers can create applications that are responsive, offline-ready, and capable of seamless data synchronization between clients and servers.
-
-## Getting Started with RxDB
-### What is RxDB?
-RxDB is an open-source JavaScript data base that leverages reactive programming and provides a seamless API for handling data. It is built on top of existing popular data base technologies, such as IndexedDB, and adds a layer of reactive features to enable real-time data updates and synchronization.
-
-### Reactive Data Handling
-One of the standout features of RxDB is its reactive data handling. It utilizes observables to provide a stream of data that automatically updates whenever a change occurs. This reactive approach allows developers to build applications that respond instantly to data changes, ensuring a highly interactive and real-time user experience.
-
-### Offline-First Approach
-RxDB embraces an offline-first approach, enabling applications to work seamlessly even when there is no internet connectivity. It achieves this by caching data locally on the client-side and synchronizing it with the server when the connection is available. This ensures that users can continue working with the application and have their data automatically synchronized when they come back online.
-
-### Data Replication
-RxDB simplifies the process of data replication between clients and servers. It provides replication plugins that handle the synchronization of data in real-time. These plugins allow applications to keep data consistent across multiple clients, enabling collaborative features and ensuring that each client has the most up-to-date information.
-
-### Observable Queries
-RxDB introduces the concept of observable queries, which are powerful tools for efficiently querying data. With observable queries, developers can subscribe to specific data queries and receive automatic updates whenever the underlying data changes. This eliminates the need for manual polling and ensures that applications always have access to the latest data.
-
-### Multi-Tab support
-RxDB offers multi-tab support, allowing applications to function seamlessly across multiple [browser](./browser-database.md) tabs. This feature ensures that data changes in one tab are immediately reflected in all other open tabs, enabling a consistent user experience across different browser windows.
-
-### RxDB vs. Other data base Options
-When considering data base options for web applications, developers often encounter choices like IndexedDB, [OPFS](../rx-storage-opfs.md), and Memory-based solutions. RxDB, while built on top of IndexedDB, stands out due to its reactive data handling capabilities and advanced synchronization features. Compared to other options, RxDB offers a more streamlined and powerful approach to managing data in web applications.
-
-### Different RxStorage layers for RxDB
-RxDB provides various [storage layers](../rx-storage.md), known as RxStorage, that serve as interfaces to different underlying [storage](./browser-storage.md) technologies. These layers include:
-
-- [LocalStorage RxStorage](../rx-storage-localstorage.md): Built on top of the browsers [localStorage API](./localstorage.md).
-- [IndexedDB RxStorage](../rx-storage-indexeddb.md): This layer directly utilizes IndexedDB as its backend, providing a robust and widely supported storage option.
-- [OPFS RxStorage](../rx-storage-opfs.md): OPFS (Operational Transformation File System) is a file system-like storage layer that allows for efficient conflict resolution and real-time collaboration.
-- Memory RxStorage: Primarily used for testing and development, this storage layer keeps data in memory without persisting it to disk.
-Each RxStorage layer has its strengths and is suited for different scenarios, enabling developers to choose the most appropriate option for their specific use case.
-
-## Synchronizing Data with RxDB between Clients and Servers
-### Offline-First Approach
-As mentioned earlier, RxDB adopts an offline-first approach, allowing applications to function seamlessly in disconnected environments. By caching data locally, applications can continue to operate and make updates even without an internet connection. Once the connection is restored, RxDB's replication plugins take care of synchronizing the data with the server, ensuring consistency across all clients.
-
-### RxDB Replication Plugins
-RxDB provides a range of replication plugins that simplify the process of synchronizing data between clients and servers. These plugins enable real-time replication using various protocols, such as WebSocket or HTTP, and handle conflict resolution strategies to ensure data integrity. By leveraging these replication plugins, developers can easily implement robust and scalable synchronization capabilities in their applications.
-
-### Advanced RxDB Features and Techniques
-Indexing and Performance Optimization
-To achieve optimal performance, RxDB offers indexing capabilities. Indexing allows for efficient data retrieval and faster query execution. By strategically defining indexes on frequently accessed fields, developers can significantly enhance the overall performance of their RxDB-powered applications.
-
-### Encryption of Local Data
-In scenarios where data security is paramount, RxDB provides options for encrypting local data. By encrypting the data base contents, developers can ensure that sensitive information remains secure even if the underlying storage is compromised. RxDB integrates seamlessly with encryption libraries, making it easy to implement end-to-end encryption in applications.
-
-### Change Streams and Event Handling
-RxDB offers change streams and event handling mechanisms, enabling developers to react to data changes in real-time. With change streams, applications can listen to specific collections or documents and trigger custom logic whenever a change occurs. This capability opens up possibilities for building real-time collaboration features, notifications, or other reactive behaviors.
-
-### JSON Key Compression
-In scenarios where storage size is a concern, RxDB provides JSON [key compression](../key-compression.md). By applying compression techniques to JSON keys, developers can significantly reduce the storage footprint of their data bases. This feature is particularly beneficial for applications dealing with large datasets or [limited storage capacities](./indexeddb-max-storage-limit.md).
-
-## Conclusion
-RxDB provides an exceptional data base solution for web and mobile applications, empowering developers to create reactive, offline-ready, and synchronized applications. With its reactive data handling, offline-first approach, and replication plugins, RxDB simplifies the challenges of building real-time applications with data synchronization requirements. By embracing advanced features like indexing, encryption, change streams, and JSON key compression, developers can optimize performance, enhance security, and reduce storage requirements. As web and [mobile applications](./mobile-database.md) continue to evolve, RxDB proves to be a reliable and powerful
-
-
In modern UI applications, efficient data storage is a crucial aspect for seamless user experiences. One powerful solution for achieving this is by utilizing an embedded database. In this article, we will explore the concept of an embedded database and delve into the benefits of using RxDB as an embedded database in UI applications. We will also discuss why RxDB stands out as a robust choice for real-time applications with embedded database functionality.
An embedded database refers to a client-side database system that is integrated directly within an application. It is designed to operate within the client environment, such as a web browser or a mobile app. This approach eliminates the need for a separate database server and allows the database to run locally on the client device.
In the context of UI applications, an embedded database serves as a local data storage solution. It enables applications to efficiently manage data, facilitate real-time updates, and enhance performance. Let's explore some of the benefits of using an embedded database compared to a traditional server database:
-
-
Replicating database state becomes easier: Implementing real-time data synchronization and replication is simpler with an embedded database compared to complex REST routes. The embedded nature allows for efficient replication of the database state across multiple instances of the application.
-
Use the database for caching: An embedded database can be utilized for caching frequently accessed data. This caching mechanism enhances performance and reduces the need for repeated network requests, resulting in faster data retrieval.
-
Building real-time applications is easier with local data: By leveraging local data storage, real-time applications can easily update the user interface in response to data changes. This approach simplifies the development of real-time features and enhances the responsiveness of the application.
-
Store local data with encryption: Embedded databases, like RxDB, offer the ability to store local data with encryption. This ensures that sensitive information remains protected even when stored locally on the client device.
-
Data is offline accessible: With an embedded database, data remains accessible even when the application is offline. Users can continue to interact with the application and access their data seamlessly, irrespective of their internet connectivity.
-
Faster initial application start time: Since the data is already stored locally, there is no need for initial data fetching from a remote server. This significantly reduces the application's startup time and allows users to engage with the application more quickly.
-
Improved scalability with local queries: Embedded databases, such as RxDB, perform queries locally on the client device instead of relying on server round-trips. This reduces latency and enhances scalability, particularly when dealing with large datasets or high query volumes.
-
Seamless integration with JavaScript frameworks: Embedded databases, including RxDB, integrate seamlessly with popular JavaScript frameworks like Angular, React.js, Vue.js, and Svelte. This compatibility allows developers to leverage the capabilities of these frameworks while benefiting from embedded database functionality.
-
Running queries locally has low latency: With an embedded database, queries are executed locally on the client device, resulting in minimal latency. This improves the overall performance and responsiveness of the application.
-
Data is portable and always accessible by the user: Embedded databases enable data portability, allowing users to seamlessly transition between devices while maintaining their data and application state. This ensures that data is always accessible and available to the user.
-
Using a local database for state management: Instead of relying on additional state management libraries like Redux or NgRx, an embedded database can be used for local state management. This simplifies state management and ensures data consistency within the application.
-
-
Why RxDB as an Embedded Database for Real-time Applications
-
RxDB is a JavaScript-based embedded database that offers numerous advantages for building real-time applications. Let's explore why RxDB is a compelling choice:
-
-
Observable Queries (RxJS): RxDB leverages the power of Observables through RxJS, enabling developers to create queries that automatically update the user interface on data changes. This reactive approach simplifies UI updates and ensures real-time synchronization of data.
-
NoSQL JSON Documents for UIs: RxDB utilizes NoSQL (JSON) documents as its data model, aligning seamlessly with the requirements of modern UI development. JavaScript's native support for JSON objects makes NoSQL documents a natural fit for UI-driven applications.
-
Better TypeScript Support Compared to SQL: RxDB's NoSQL approach provides excellent TypeScript support. The flexibility of working with JSON objects enables robust typing and enhanced development experiences, ensuring type safety and reducing runtime errors.
-
Observable Document Fields: RxDB allows developers to observe individual fields within documents. This granularity enables efficient tracking of specific data changes and facilitates targeted UI updates, enhancing performance and responsiveness.
-
Made in JavaScript, Optimized for JavaScript Applications: Being built entirely in JavaScript, RxDB is optimized for JavaScript applications. It leverages JavaScript's capabilities and integrates seamlessly with JavaScript frameworks and libraries, making it a natural choice for JavaScript developers.
-
Optimized Observed Queries with the EventReduce Algorithm: RxDB incorporates the EventReduce algorithm to optimize observed queries. This algorithm reduces the number of emitted events during query execution, resulting in enhanced query performance and reduced overhead.
-
Built-in Multi-tab Support: RxDB provides built-in multi-tab support, allowing multiple instances of an application to share and synchronize data seamlessly. This feature enables collaborative and real-time scenarios across multiple browser tabs or windows.
-
Handling of Schema Changes across Multiple Client Devices: With RxDB, handling schema changes across multiple client devices becomes straightforward. RxDB's schema migration capabilities ensure that applications can seamlessly adapt to evolving data structures, providing a consistent experience across different devices.
-
Storing Documents Compressed: RxDB offers the ability to store documents in a compressed format. This reduces the storage footprint and improves performance, especially when dealing with large datasets.
-
Flexible Storage Layer and Cross-platform Compatibility: RxDB provides a flexible storage layer that can be reused across various platforms, including Electron.js, React Native, hybrid apps (via Capacitor.js), and browsers. This cross-platform compatibility simplifies development and enables code reuse across different environments.
-
Replication Algorithm for Backend Compatibility: RxDB's replication algorithm is open-source and can be made compatible with various backend solutions, such as self-hosted servers, Firebase, CouchDB, NATS, WebSockets, and more. This flexibility allows developers to choose their preferred backend infrastructure while benefiting from RxDB's embedded database capabilities.
To further explore RxDB and leverage its capabilities as an embedded database, the following resources can be helpful:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-
-
By utilizing RxDB as an embedded database in UI applications, developers can harness the power of efficient data management, real-time updates, and enhanced user experiences. RxDB's features and benefits make it a compelling choice for building modern, responsive, and scalable applications.
-
-
\ No newline at end of file
diff --git a/docs/articles/embedded-database.md b/docs/articles/embedded-database.md
deleted file mode 100644
index ccfa9468708..00000000000
--- a/docs/articles/embedded-database.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Embedded Database, Real-time Speed - RxDB
-
-> Unleash the power of embedded databases with RxDB. Explore real-time replication, offline access, and reactive queries for modern JavaScript apps.
-
-# Using RxDB as an Embedded Database
-In modern UI applications, efficient data storage is a crucial aspect for seamless user experiences. One powerful solution for achieving this is by utilizing an embedded database. In this article, we will explore the concept of an embedded database and delve into the benefits of using [RxDB](https://rxdb.info/) as an embedded database in UI applications. We will also discuss why RxDB stands out as a robust choice for real-time applications with embedded database functionality.
-
-
-
-
-
-
-
-## What is an Embedded Database?
-An embedded database refers to a client-side database system that is integrated directly within an application. It is designed to operate within the client environment, such as a web browser or a [mobile](./mobile-database.md) app. This approach eliminates the need for a separate database server and allows the database to run locally on the client device.
-
-## Embedded Database in UI Applications
-In the context of UI applications, an embedded database serves as a local data storage solution. It enables applications to efficiently manage data, facilitate real-time updates, and enhance performance. Let's explore some of the benefits of using an embedded database compared to a traditional server database:
-
-- Replicating database state becomes easier: Implementing real-time data synchronization and replication is simpler with an embedded database compared to complex REST routes. The embedded nature allows for efficient replication of the database state across multiple instances of the application.
-- Use the database for caching: An embedded database can be utilized for caching frequently accessed data. This caching mechanism enhances performance and reduces the need for repeated network requests, resulting in faster data retrieval.
-- Building real-time applications is easier with local data: By leveraging local data storage, real-time applications can easily update the user interface in response to data changes. This approach simplifies the development of real-time features and enhances the responsiveness of the application.
-- Store local data with [encryption](../encryption.md): Embedded databases, like RxDB, offer the ability to store local data with encryption. This ensures that sensitive information remains protected even when stored locally on the client device.
-- Data is offline accessible: With an embedded database, data remains accessible even when the application is offline. Users can continue to interact with the application and access their data seamlessly, irrespective of their internet connectivity.
-- Faster initial application start time: Since the data is already stored locally, there is no need for initial data fetching from a remote server. This significantly reduces the application's startup time and allows users to engage with the application more quickly.
-- Improved scalability with local queries: Embedded databases, such as RxDB, perform queries locally on the client device instead of relying on server round-trips. This reduces latency and enhances scalability, particularly when dealing with large datasets or high query volumes.
-- Seamless integration with JavaScript frameworks: Embedded databases, including RxDB, integrate seamlessly with popular JavaScript frameworks like Angular, React.js, [Vue.js](./vue-database.md), and Svelte. This compatibility allows developers to leverage the capabilities of these frameworks while benefiting from embedded database functionality.
-- Running queries locally has low latency: With an embedded database, queries are executed locally on the client device, resulting in minimal latency. This improves the overall performance and responsiveness of the application.
-- Data is portable and always accessible by the user: Embedded databases enable data portability, allowing users to seamlessly transition between devices while maintaining their data and application state. This ensures that data is always accessible and available to the user.
-- Using a [local database](./local-database.md) for state management: Instead of relying on additional state management libraries like Redux or NgRx, an embedded database can be used for local state management. This simplifies state management and ensures data consistency within the application.
-
-## Why RxDB as an Embedded Database for Real-time Applications
-RxDB is a JavaScript-based embedded database that offers numerous advantages for building real-time applications. Let's explore why RxDB is a compelling choice:
-
-- [Observable Queries](../rx-query.md) (RxJS): RxDB leverages the power of Observables through RxJS, enabling developers to create queries that automatically update the user interface on data changes. This reactive approach simplifies UI updates and ensures real-time synchronization of data.
-- [NoSQL JSON Documents](./json-database.md) for UIs: RxDB utilizes NoSQL (JSON) documents as its data model, aligning seamlessly with the requirements of modern UI development. JavaScript's native support for JSON objects makes NoSQL documents a natural fit for UI-driven applications.
-- Better TypeScript Support Compared to SQL: RxDB's NoSQL approach provides excellent TypeScript support. The flexibility of working with JSON objects enables robust typing and enhanced development experiences, ensuring type safety and reducing runtime errors.
-- [Observable Document Fields](../rx-document.md): RxDB allows developers to observe individual fields within documents. This granularity enables efficient tracking of specific data changes and facilitates targeted UI updates, enhancing performance and responsiveness.
-- Made in JavaScript, Optimized for JavaScript Applications: Being built entirely in JavaScript, RxDB is optimized for JavaScript applications. It leverages JavaScript's capabilities and integrates seamlessly with JavaScript frameworks and libraries, making it a natural choice for JavaScript developers.
-- Optimized Observed Queries with the [EventReduce Algorithm](https://github.com/pubkey/event-reduce): RxDB incorporates the EventReduce algorithm to optimize observed queries. This algorithm reduces the number of emitted events during query execution, resulting in enhanced query performance and reduced overhead.
-- Built-in Multi-tab Support: RxDB provides built-in multi-tab support, allowing multiple instances of an application to share and synchronize data seamlessly. This feature enables collaborative and real-time scenarios across multiple browser tabs or windows.
-- Handling of Schema Changes across Multiple Client Devices: With RxDB, handling schema changes across multiple client devices becomes straightforward. RxDB's schema [migration capabilities](../migration-schema.md) ensure that applications can seamlessly adapt to evolving data structures, providing a consistent experience across different devices.
-- Storing Documents Compressed: RxDB offers the ability to store documents in a compressed format. This reduces the storage footprint and improves performance, especially when dealing with large datasets.
-- Flexible Storage Layer and Cross-platform Compatibility: RxDB provides a flexible storage layer that can be reused across various platforms, including [Electron.js](../electron-database.md), [React Native](../react-native-database.md), hybrid apps (via Capacitor.js), and browsers. This cross-platform compatibility simplifies development and enables code reuse across different environments.
-- Replication Algorithm for Backend Compatibility: RxDB's replication algorithm is open-source and can be made compatible with various backend solutions, such as self-hosted servers, Firebase, [CouchDB](../replication-couchdb.md), NATS, WebSockets, and more. This flexibility allows developers to choose their preferred backend infrastructure while benefiting from RxDB's embedded database capabilities.
-
-
-
-
-
-
-
-## Follow Up
-To further explore [RxDB](https://rxdb.info/) and leverage its capabilities as an embedded database, the following resources can be helpful:
-
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-
-By utilizing [RxDB](https://rxdb.info/) as an embedded database in UI applications, developers can harness the power of efficient data management, real-time updates, and enhanced user experiences. RxDB's features and benefits make it a compelling choice for building modern, responsive, and scalable applications.
diff --git a/docs/articles/firebase-realtime-database-alternative.html b/docs/articles/firebase-realtime-database-alternative.html
deleted file mode 100644
index 9fde2225356..00000000000
--- a/docs/articles/firebase-realtime-database-alternative.html
+++ /dev/null
@@ -1,199 +0,0 @@
-
-
-
-
-
-RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
RxDB - The Firebase Realtime Database Alternative That Can Sync With Your Own Backend
-
Are you on the lookout for a Firebase Realtime Database alternative that gives you greater freedom, deeper offline capabilities, and allows you to seamlessly integrate with any backend? RxDB (Reactive Database) might be the perfect choice. This local-first, NoSQL data store runs entirely on the client while supporting real-time updates and robust syncing with any server environment—making it a strong contender against Firebase Realtime Database's limitations and potential vendor lock-in.
-
-
Why RxDB Is an Excellent Firebase Realtime Database Alternative
Unlike Firebase Realtime Database, which relies on central infrastructure to process data, RxDB is fully embedded within your client application (including browsers, Node.js, Electron, and React Native). This design means your app stays completely functional offline, since all data reads and writes happen locally. When connectivity is restored, RxDB's syncing framework automatically reconciles local changes with your remote backend.
Firebase Realtime Database typically updates data with a simple last-in-wins approach. RxDB, on the other hand, lets you implement more sophisticated conflict resolution logic. Using revisions and conflict handlers, RxDB can merge concurrent edits or preserve multiple versions—ensuring your application remains consistent even when multiple clients modify the same data at the same time.
When you rely on Firebase Realtime Database, each query or listener can translate into ongoing reads, potentially running up your monthly bill. With RxDB, all queries are performed locally. Your app only communicates with the backend to sync document changes, significantly reducing bandwidth and hosting expenses for applications that frequently read data.
While Firebase offers some offline caching, it often requires an initial connection for authentication or to seed local data. RxDB, however, is built to handle an offline-start scenario. Users can begin working with the application immediately, regardless of connectivity, and any modifications they make will sync once the network is available again.
One of RxDB's core strengths is its ability to run in any JavaScript environment. Whether you're building a web app that uses IndexedDB in the browser, an Electron desktop program, or a React Native mobile application, RxDB's swappable storage adapts to your runtime of choice. This consistency makes code-sharing and cross-platform development far simpler than being tied to a single backend system.
Long Offline Use: If your users need to work without an internet connection, RxDB's built-in offline-first design stands out compared to Firebase Realtime Database's partial offline approach.
-
Custom or Complex Queries: RxDB lets you perform your queries locally, define indexing, and handle even complex transformations locally - no extra call to an external API.
-
Avoid Vendor Lock-In: If you anticipate needing to move or adapt your backend later, you can do so without rewriting how your client manages its data.
-
Peer-to-Peer Collaboration: Whether you need quick demos or real production use, WebRTC replication can link your users directly without central coordination of data storage.
-
-
\ No newline at end of file
diff --git a/docs/articles/firebase-realtime-database-alternative.md b/docs/articles/firebase-realtime-database-alternative.md
deleted file mode 100644
index aa8ae045a60..00000000000
--- a/docs/articles/firebase-realtime-database-alternative.md
+++ /dev/null
@@ -1,190 +0,0 @@
-# RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend
-
-> Looking for a Firebase Realtime Database alternative? RxDB offers a fully offline, vendor-agnostic NoSQL solution with advanced conflict resolution and multi-platform support.
-
-# RxDB - The Firebase Realtime Database Alternative That Can Sync With Your Own Backend
-
-Are you on the lookout for a **Firebase Realtime Database alternative** that gives you greater freedom, deeper offline capabilities, and allows you to seamlessly integrate with any backend? **RxDB** (Reactive Database) might be the perfect choice. This [local-first](./local-first-future.md), NoSQL data store runs entirely on the client while supporting real-time updates and robust syncing with any server environment—making it a strong contender against Firebase Realtime Database's limitations and potential vendor lock-in.
-
-
-
-
-
-
-
-## Why RxDB Is an Excellent Firebase Realtime Database Alternative
-
-### 1. Complete Offline-First Experience
-Unlike Firebase Realtime Database, which relies on central infrastructure to process data, RxDB is fully embedded within your client application (including [browsers](./browser-database.md), [Node.js](../nodejs-database.md), [Electron](../electron-database.md), and [React Native](../react-native-database.md)). This design means your app stays completely functional offline, since all data reads and writes happen locally. When connectivity is restored, RxDB's syncing framework automatically reconciles local changes with your remote backend.
-
-### 2. Freedom to Use Any Server or Cloud
-While Firebase Realtime Database ties you into Google's ecosystem, RxDB allows you to choose any hosting environment. You can:
-- Host your data on your own servers or private cloud.
-- Integrate with relational databases like [PostgreSQL](../replication-http.md) or other NoSQL options such as [CouchDB](../replication-couchdb.md).
-- Build custom endpoints using [REST](../replication-http.md), [GraphQL](../replication-graphql.md), or any other protocol.
-
-This flexibility ensures you're not locked into a single vendor and can adapt your backend strategy as your project evolves.
-
-### 3. Advanced Conflict Handling
-Firebase Realtime Database typically updates data with a simple last-in-wins approach. RxDB, on the other hand, lets you implement more sophisticated conflict resolution logic. Using [revisions and conflict handlers](../transactions-conflicts-revisions.md#custom-conflict-handler), RxDB can merge concurrent edits or preserve multiple versions—ensuring your application remains consistent even when multiple clients modify the same data at the same time.
-
-### 4. Lower Cloud Costs for Read-Heavy Apps
-When you rely on Firebase Realtime Database, each query or listener can translate into ongoing reads, potentially running up your monthly bill. With RxDB, all queries are performed [locally](../offline-first.md). Your app only communicates with the backend to sync document changes, significantly reducing bandwidth and hosting expenses for applications that frequently read data.
-
-### 5. Powerful Local Queries
-If you've hit Firebase Realtime Database's querying limits, RxDB offers a far more robust approach to data retrieval. You can:
-- Define custom indexes for faster local lookups.
-- Perform sophisticated filters, joins, or full-text searches right on the client.
-- Subscribe to real-time data updates through RxDB's [reactive query engine](../reactivity.md).
-
-Because these operations happen locally, your [UI updates](./optimistic-ui.md) instantly, providing a snappy user experience.
-
-### 6. True Offline Initialization
-While Firebase offers some offline caching, it often requires an initial connection for authentication or to seed local data. RxDB, however, is built to handle an **offline-start** scenario. Users can begin working with the application immediately, regardless of connectivity, and any modifications they make will sync once the network is available again.
-
-### 7. Works Everywhere JavaScript Runs
-One of RxDB's core strengths is its ability to run in **any JavaScript environment**. Whether you're building a web app that uses IndexedDB in the browser, an [Electron](../electron-database.md) desktop program, or a [React Native](../react-native-database.md) mobile application, RxDB's **swappable storage** adapts to your runtime of choice. This consistency makes code-sharing and cross-platform development far simpler than being tied to a single backend system.
-
----
-
-## How RxDB's Syncing Mechanism Operates
-
-RxDB employs its own [Sync Engine](../replication.md) to manage data flow between your client and remote [servers](../rx-server.md). Replication revolves around:
-1. **Pull**: Retrieving updated or newly created documents from the server.
-2. **Push**: Sending local changes to the backend for persistence.
-3. **Live Updates**: Continuously streaming changes to and from the backend for real-time synchronization.
-
-## Sample Code: Sync RxDB With a Custom Endpoint
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { replicateRxCollection } from 'rxdb/plugins/replication';
-
-async function initDB() {
- const db = await createRxDatabase({
- name: 'localdb',
- storage: getRxStorageLocalstorage(),
- multiInstance: true,
- eventReduce: true
- });
-
- await db.addCollections({
- tasks: {
- schema: {
- title: 'task schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string', maxLength: 100 },
- title: { type: 'string' },
- complete: { type: 'boolean' }
- }
- }
- }
- });
-
- // Start a custom replication
- replicateRxCollection({
- collection: db.tasks,
- replicationIdentifier: 'custom-tasks-api',
- push: {
- handler: async (docs) => {
- // post local changes to your server
- const resp = await fetch('https://yourapi.com/tasks/push', {
- method: 'POST',
- body: JSON.stringify({ changes: docs })
- });
- return await resp.json(); // return conflicting documents if any
- }
- },
- pull: {
- handler: async (lastCheckpoint, batchSize) => {
- // fetch new/updated items from your server
- const response = await fetch(
- `https://yourapi.com/tasks/pull?checkpoint=${JSON.stringify(
- lastCheckpoint
- )}&limit=${batchSize}`
- );
- return await response.json();
- }
- },
- live: true
- });
-
- return db;
-}
-```
-
-### Setting Up P2P Replication Over WebRTC
-In addition to using a centralized backend, RxDB supports peer-to-peer synchronization through WebRTC, enabling devices to share data directly.
-
-```ts
-import {
- replicateWebRTC,
- getConnectionHandlerSimplePeer
-} from 'rxdb/plugins/replication-webrtc';
-
-const webrtcPool = await replicateWebRTC({
- collection: db.tasks,
- topic: 'p2p-topic-123',
- connectionHandlerCreator: getConnectionHandlerSimplePeer({
- signalingServerUrl: 'wss://signaling.rxdb.info/',
- wrtc: require('node-datachannel/polyfill'),
- webSocketConstructor: require('ws').WebSocket
- })
-});
-
-webrtcPool.error$.subscribe((error) => {
- console.error('P2P error:', error);
-});
-```
-
-Here, any client that joins the same topic communicates changes to other peers, all without requiring a traditional client-server model.
-
-## Quick Steps to Get Started
-
-1. Install RxDB
-```bash
-npm install rxdb rxjs
-```
-
-2. Create a Local Database
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-const db = await createRxDatabase({
- name: 'myLocalDB',
- storage: getRxStorageLocalstorage()
-});
-Add a Collection
-ts
-Kopieren
-await db.addCollections({
- notes: {
- schema: {
- title: 'notes schema',
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: { type: 'string', maxLenght: 100 },
- content: { type: 'string' }
- }
- }
- }
-});
-```
-
-3. Synchronize
-
-Use one of the [Replication Plugins](../replication.md) to connect with your preferred backend.
-
-### Is RxDB the Right Solution for You?
-
-- **Long Offline Use**: If your users need to work without an internet connection, RxDB's built-in offline-first design stands out compared to Firebase Realtime Database's partial offline approach.
-- **Custom or Complex Queries**: RxDB lets you perform your [queries](../rx-query.md) locally, define [indexing](../rx-schema.md#indexes), and handle even complex [transformations](../rx-pipeline.md) locally - no extra call to an external API.
-- **Avoid Vendor Lock-In**: If you anticipate needing to move or adapt your backend later, you can do so without rewriting how your client manages its data.
-- **Peer-to-Peer Collaboration**: Whether you need quick demos or real production use, [WebRTC replication](../replication-webrtc.md) can link your users directly without central coordination of data storage.
diff --git a/docs/articles/firestore-alternative.html b/docs/articles/firestore-alternative.html
deleted file mode 100644
index 18ee053a1be..00000000000
--- a/docs/articles/firestore-alternative.html
+++ /dev/null
@@ -1,225 +0,0 @@
-
-
-
-
-
-RxDB - Firestore Alternative to Sync with Your Own Backend | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Enter RxDB (Reactive Database) - a local-first, NoSQL database for JavaScript applications that can sync in real time with any backend of your choice. Whether you're tired of the limitations and fees associated with Firebase Cloud Firestore or simply need more flexibility, RxDB might be the Firestore alternative you've been searching for.
Firestore is convenient for many projects, but it does lock you into Google's ecosystem. Below are some of the key advantages you gain by choosing RxDB:
RxDB runs directly in your client application (browser, Node.js, Electron, React Native, etc.). Data is stored locally, so your application remains fully functional even when offline. When the device returns online, RxDB's flexible replication protocol synchronizes your local changes with any remote endpoint.
This backend-agnostic approach protects you from vendor lock-in. Your application's client-side data storage remains consistent; only your replication logic (or plugin) changes if you switch servers.
Firestore enforces a last-write-wins conflict resolution strategy. This might cause issues if multiple users or devices update the same data in complex ways.
-
RxDB lets you:
-
-
Implement custom conflict resolution via revisions.
-
Store partial merges, track versions, or preserve multiple user edits.
-
Fine-tune how your data merges to ensure consistency across distributed systems.
Firestore queries often count as billable reads. With RxDB, queries run locally against your local state - no repeated network calls or extra charges. You pay only for the data actually synced, not every read. For read-heavy apps, using RxDB as a Firestore alternative can significantly reduce costs.
While Firestore does have offline caching, it often requires an online check at app initialization for authentication. RxDB is truly offline-first; you can launch the app and write data even if the device never goes online initially. It's ready whenever the user is.
RxDB is designed to run in any environment that can execute JavaScript. Whether you’re building a web app in the browser, an Electron desktop application, a React Native mobile app, or a command-line tool with Node.js, RxDB’s storage layer is swappable to fit your runtime’s capabilities.
By swapping out the handler implementations or using an official plugin (e.g., GraphQL, CouchDB, Firestore replication, etc.), you can adapt to any backend or data source. RxDB thus becomes a flexible alternative to Firestore while maintaining real-time capabilities.
-
Getting Started with RxDB as a Firestore Alternative
Use a Replication Plugin to connect with a custom backend or existing database.
-
For a Firestore-specific approach, RxDB Firestore Replication also exists if you want to combine local indexing and advanced queries with a Cloud Firestore backend. But if you really want to replace Firestore entirely - just point RxDB to your new backend.
In addition to syncing with a central server, RxDB also supports pure peer-to-peer replication using WebRTC. This can be invaluable for scenarios where clients need to sync data directly without a master server.
-
import {
- replicateWebRTC,
- getConnectionHandlerSimplePeer
-} from 'rxdb/plugins/replication-webrtc';
-
-const replicationPool = await replicateWebRTC({
- collection: db.tasks,
- topic: 'my-p2p-room', // Clients with the same topic will sync with each other.
- connectionHandlerCreator: getConnectionHandlerSimplePeer({
- // Use your own or the official RxDB signaling server
- signalingServerUrl: 'wss://signaling.rxdb.info/',
-
- // Node.js requires a polyfill for WebRTC & WebSocket
- wrtc: require('node-datachannel/polyfill'),
- webSocketConstructor: require('ws').WebSocket
- }),
- pull: {}, // optional pull config
- push: {} // optional push config
-});
-
-// The replicationPool manages all connected peers
-replicationPool.error$.subscribe(err => {
- console.error('P2P Sync Error:', err);
-});
-
This example sets up a live P2P replication where any new peers joining the same topic automatically sync local data with each other, eliminating the need for a dedicated central server for the actual data exchange.
You want offline-first: If you need an offline-first app that starts offline, RxDB's local database approach and sync protocol excel at this.
-
Your project is read-heavy: Reading from Firestore for every query can get expensive. With RxDB, reads are free and local; you only pay for writes or sync overhead.
-
You need advanced queries: Firestore's query constraints may not suit complex data. With RxDB, you can define your own indexing logic or run arbitrary queries locally.
-
You want no vendor lock-in: Easily transition from Firestore to your own server or another vendor - just change the replication layer.
If you've been searching for a Firestore alternative that gives you the freedom to sync your data with any backend, offers robust offline-first capabilities, and supports truly customizable conflict resolution and queries, RxDB is worth exploring. You can adopt it seamlessly, ensure local reads, reduce costs, and stay in complete control of your data layer.
-
Ready to dive in? Check out the RxDB Quickstart Guide, join our Discord community, and experience how RxDB can be the perfect local-first, real-time database solution for your next project.
-
-
\ No newline at end of file
diff --git a/docs/articles/firestore-alternative.md b/docs/articles/firestore-alternative.md
deleted file mode 100644
index 0027a2aa25c..00000000000
--- a/docs/articles/firestore-alternative.md
+++ /dev/null
@@ -1,225 +0,0 @@
-# RxDB - Firestore Alternative to Sync with Your Own Backend
-
-> Looking for a Firestore alternative? RxDB is a local-first, NoSQL database that syncs seamlessly with any backend, offers rich offline capabilities, advanced conflict resolution, and reduces vendor lock-in.
-
-# RxDB - The Firestore Alternative That Can Sync with Your Own Backend
-
-If you're seeking a **Firestore alternative**, you're likely looking for a way to:
-- **Avoid vendor lock-in** while still enjoying real-time replication.
-- **Reduce cloud usage costs** by reading data locally instead of constantly fetching from the server.
-- **Customize** how you store, query, and secure your data.
-- **Implement advanced conflict resolution** strategies beyond Firestore's last-write-wins approach.
-
-Enter **RxDB** (Reactive Database) - a [local-first](./local-first-future.md), NoSQL database for JavaScript applications that can sync in real time with **any** backend of your choice. Whether you're tired of the limitations and fees associated with Firebase Cloud Firestore or simply need more flexibility, RxDB might be the Firestore alternative you've been searching for.
-
-
-
-
-
-
-
-## What Makes RxDB a Great Firestore Alternative?
-
-Firestore is convenient for many projects, but it does lock you into Google's ecosystem. Below are some of the key advantages you gain by choosing RxDB:
-
-### 1. Fully Offline-First
-RxDB runs directly in your client application ([browser](./browser-database.md), [Node.js](../nodejs-database.md), [Electron](../electron-database.md), [React Native](../react-native-database.md), etc.). Data is stored locally, so your application **remains fully functional even when offline**. When the device returns online, RxDB's flexible replication protocol synchronizes your local changes with any remote endpoint.
-
-### 2. Freedom to Use Any Backend
-Unlike Firestore, RxDB doesn't require a proprietary hosting service. You can:
-- Host your data on your own server (Node.js, Go, Python, etc.).
-- Use existing databases like [PostgreSQL](../replication-http.md), [CouchDB](../replication-couchdb.md), or [MongoDB with custom endpoints](../replication.md).
-- Implement a [custom GraphQL](../replication-graphql.md) or [REST-based](../replication-http.md) API for syncing.
-
-This **backend-agnostic** approach protects you from vendor lock-in. Your application's client-side data storage remains consistent; only your replication logic (or plugin) changes if you switch servers.
-
-### 3. Advanced Conflict Resolution
-Firestore enforces a [last-write-wins](https://stackoverflow.com/a/47781502/3443137) conflict resolution strategy. This might cause issues if multiple users or devices update the same data in complex ways.
-
-RxDB lets you:
-- Implement **custom conflict resolution** via [revisions](../transactions-conflicts-revisions.md#custom-conflict-handler).
-- Store partial merges, track versions, or preserve multiple user edits.
-- Fine-tune how your data merges to ensure consistency across distributed systems.
-
-### 4. Reduced Cloud Costs
-Firestore queries often count as billable reads. With RxDB, queries run **locally** against your local state - no repeated network calls or extra charges. You pay only for the data actually synced, not every read. For **read-heavy** apps, using RxDB as a Firestore alternative can significantly reduce costs.
-
-### 5. No Limits on Query Features
-Firestore's query engine is limited by certain constraints (e.g., no advanced joins, limited indexing). With RxDB:
-- **NoSQL** data is stored locally, and you can define any indexes you need.
-- Perform [complex queries](../rx-query.md), run [full-text search](../fulltext-search.md), or do aggregated transformations or even [vector search](./javascript-vector-database.md).
-- Use [RxDB's reactivity](../rx-query.md#observe) to subscribe to query results in real time.
-
-### 6. True Offline-Start Support
-While Firestore does have offline caching, it often requires an online check at app initialization for authentication. RxDB is [truly offline-first](../offline-first.md); you can launch the app and write data even if the device never goes online initially. It's ready whenever the user is.
-
-### 7. Cross-Platform: Any JavaScript Runtime
-RxDB is designed to run in **any environment** that can execute JavaScript. Whether you’re building a web app in the browser, an [Electron](../electron-database.md) desktop application, a [React Native](../react-native-database.md) mobile app, or a command-line tool with [Node.js](../nodejs-database.md), RxDB’s storage layer is swappable to fit your runtime’s capabilities.
-- In the **browser**, store data in [IndexedDB](../rx-storage-indexeddb.md) or [OPFS](../rx-storage-opfs.md).
-- In [Node.js](../nodejs-database.md), use LevelDB or other supported storages.
-- In [React Native](../react-native-database.md), pick from a range of adapters suited for mobile devices.
-- In [Electron](../electron-database.md), rely on fast local storage with zero changes to your application code.
-
----
-
-## How Does RxDB's Sync Work?
-
-RxDB replication is powered by its own [Sync Engine](../replication.md). This simple yet robust protocol enables:
-1. **Pull**: Fetch new or updated documents from the server.
-2. **Push**: Send local changes back to the server.
-3. **Live Real-Time**: Once you're caught up, you can opt for event-based streaming instead of continuous polling.
-
-Code Example: Sync RxDB with a Custom Backend
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { replicateRxCollection } from 'rxdb/plugins/replication';
-
-async function initDB() {
- const db = await createRxDatabase({
- name: 'mydb',
- storage: getRxStorageLocalstorage(),
- multiInstance: true,
- eventReduce: true
- });
-
- await db.addCollections({
- tasks: {
- schema: {
- title: 'task schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string', maxLength: 100 },
- title: { type: 'string' },
- done: { type: 'boolean' }
- }
- }
- }
- });
-
- // Start a custom REST-based replication
- replicateRxCollection({
- collection: db.tasks,
- replicationIdentifier: 'my-tasks-rest-api',
- push: {
- handler: async (documents) => {
- // Send docs to your REST endpoint
- const res = await fetch('https://myapi.com/push', {
- method: 'POST',
- body: JSON.stringify({ docs: documents })
- });
- // Return conflicts if any
- return await res.json();
- }
- },
- pull: {
- handler: async (lastCheckpoint, batchSize) => {
- // Fetch from your REST endpoint
- const res = await fetch(`https://myapi.com/pull?checkpoint=${JSON.stringify(lastCheckpoint)}&limit=${batchSize}`);
- return await res.json();
- }
- },
- live: true // keep watching for changes
- });
-
- return db;
-}
-```
-
-By swapping out the handler implementations or using an official plugin (e.g., [GraphQL](../replication-graphql.md), [CouchDB](../replication-couchdb.md), [Firestore replication](../replication-firestore.md), etc.), you can adapt to any backend or data source. RxDB thus becomes a flexible alternative to Firestore while maintaining [real-time capabilities](./realtime-database.md).
-
-## Getting Started with RxDB as a Firestore Alternative
-
-### Install RxDB:
-```bash
-npm install rxdb rxjs
-```
-
-### Create a Database:
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-const db = await createRxDatabase({
- name: 'mydb',
- storage: getRxStorageLocalstorage()
-});
-```
-
-### Define Collections:
-```ts
-await db.addCollections({
- items: {
- schema: {
- title: 'items schema',
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: { type: 'string', maxLength: 100 },
- text: { type: 'string' }
- }
- }
- }
-});
-```
-
-### Sync
-Use a [Replication Plugin](../replication.md) to connect with a custom backend or existing database.
-
-For a Firestore-specific approach, RxDB [Firestore Replication](../replication-firestore.md) also exists if you want to combine local indexing and advanced queries with a Cloud Firestore backend. But if you really want to replace Firestore entirely - just point RxDB to your new backend.
-
-### Example: Start a WebRTC P2P Replication
-
-In addition to syncing with a central server, RxDB also supports pure peer-to-peer replication using [WebRTC](../replication-webrtc.md). This can be invaluable for scenarios where clients need to sync data directly without a master server.
-
-```ts
-import {
- replicateWebRTC,
- getConnectionHandlerSimplePeer
-} from 'rxdb/plugins/replication-webrtc';
-
-const replicationPool = await replicateWebRTC({
- collection: db.tasks,
- topic: 'my-p2p-room', // Clients with the same topic will sync with each other.
- connectionHandlerCreator: getConnectionHandlerSimplePeer({
- // Use your own or the official RxDB signaling server
- signalingServerUrl: 'wss://signaling.rxdb.info/',
-
- // Node.js requires a polyfill for WebRTC & WebSocket
- wrtc: require('node-datachannel/polyfill'),
- webSocketConstructor: require('ws').WebSocket
- }),
- pull: {}, // optional pull config
- push: {} // optional push config
-});
-
-// The replicationPool manages all connected peers
-replicationPool.error$.subscribe(err => {
- console.error('P2P Sync Error:', err);
-});
-```
-
-This example sets up a live **P2P replication** where any new peers joining the same topic automatically sync local data with each other, eliminating the need for a dedicated central server for the actual data exchange.
-
-## Is RxDB Right for Your Project?
-
-- **You want offline-first**: If you need an offline-first app that starts offline, RxDB's local database approach and sync protocol excel at this.
-- **Your project is read-heavy**: Reading from Firestore for every query can get expensive. With RxDB, reads are free and local; you only pay for writes or sync overhead.
-- **You need advanced queries**: Firestore's query constraints may not suit complex data. With RxDB, you can define your own indexing logic or run arbitrary queries locally.
-- **You want no vendor lock-in**: Easily transition from Firestore to your own server or another vendor - just change the replication layer.
-
-## Follow Up
-
-If you've been searching for a Firestore alternative that gives you the freedom to sync your data with any backend, offers robust offline-first capabilities, and supports truly customizable conflict resolution and queries, RxDB is worth exploring. You can adopt it seamlessly, ensure local reads, reduce costs, and stay in complete control of your data layer.
-
-Ready to dive in? Check out the RxDB Quickstart Guide, join our Discord community, and experience how RxDB can be the perfect local-first, real-time database solution for your next project.
-
-More resources:
-- [RxDB Sync Engine](../replication.md)
-- [Firestore Replication Plugin](../replication-firestore.md)
-- [Custom Conflict Resolution](../transactions-conflicts-revisions.md)
-- [RxDB GitHub Repository](/code/)
diff --git a/docs/articles/flutter-database.html b/docs/articles/flutter-database.html
deleted file mode 100644
index 02db43cfaa6..00000000000
--- a/docs/articles/flutter-database.html
+++ /dev/null
@@ -1,188 +0,0 @@
-
-
-
-
-
-Supercharge Flutter Apps with the RxDB Database | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In the world of mobile application development, Flutter has gained significant popularity due to its cross-platform capabilities and rich UI framework. When it comes to building feature-rich Flutter applications, the choice of a robust and efficient database is crucial. In this article, we will explore RxDB as a database solution for Flutter applications. We'll delve into the core features of RxDB, its benefits over other database options, and how to integrate it into a Flutter app.
-
note
You can find the source code for an example RxDB Flutter Application at the github repo
Flutter is an open-source UI software development kit created by Google that allows developers to build high-performance mobile applications for iOS and Android platforms using a single codebase. Flutter's framework provides a wide range of widgets and tools that enable developers to create visually appealing and responsive applications.
Databases play a vital role in Flutter applications by providing a persistent and reliable storage solution for storing and retrieving data. Whether it's user profiles, app settings, or complex data structures, a database helps in efficiently managing and organizing the application's data. Choosing the right database for a Flutter application can significantly impact the performance, scalability, and user experience of the app.
RxDB is a powerful NoSQL database solution that is designed to work seamlessly with JavaScript-based frameworks, such as Flutter. It stands for Reactive Database and offers a variety of features that make it an excellent choice for building Flutter applications. RxDB combines the simplicity of JavaScript's document-based database model with the reactive programming paradigm, enabling developers to build real-time and offline-first applications with ease.
RxDB is a client-side database built on top of IndexedDB, which is a low-level browser-based database API. It provides a simple and intuitive API for performing CRUD operations (Create, Read, Update, Delete) on documents. RxDB's underlying architecture allows for efficient handling of data synchronization between multiple clients and servers.
One of the key strengths of RxDB is its reactive data handling. It leverages the power of Observables, a concept from reactive programming, to automatically update the UI in response to data changes. With RxDB, developers can define queries and subscribe to their results, ensuring that the UI is always in sync with the database.
RxDB follows an offline-first approach, making it ideal for building Flutter applications that need to function even without an internet connection. It allows data to be stored locally and seamlessly synchronizes it with the server when a connection is available. This ensures that users can access and interact with their data regardless of network availability.
Data replication is a critical aspect of building distributed applications. RxDB provides robust replication capabilities that enable synchronization of data between different clients and servers. With its replication plugins, RxDB simplifies the process of setting up real-time data synchronization, ensuring consistency across all connected devices.
RxDB introduces the concept of observable queries, which are queries that automatically update when the underlying data changes. This feature is particularly useful for keeping the UI up to date with the latest data. By subscribing to an observable query, developers can receive real-time updates and reflect them in the user interface without manual intervention.
When considering database options for Flutter applications, developers often come across alternatives such as SQLite or LokiJS. While these databases have their merits, RxDB offers several advantages over them. RxDB's seamless integration with Flutter, its offline-first approach, reactive data handling, and built-in data replication make it a compelling choice for building feature-rich and scalable Flutter applications.
RxDB is written in TypeScript and compiled to JavaScript. To run it in a Flutter application, the flutter_qjs library is used to spawn a QuickJS JavaScript runtime. RxDB itself runs in that runtime and communicates with the flutter dart runtime. To store data persistent, the LokiJS RxStorage is used together with a custom storage adapter that persists the database inside of the shared_preferences data.
-
To use RxDB, you have to create a compatible JavaScript file that creates your RxDatabase and starts some connectors which are used by Flutter to communicate with the JavaScript RxDB database via setFlutterRxDatabaseConnector().
-
import {
- createRxDatabase
-} from 'rxdb';
-import {
- getRxStorageLoki
-} from 'rxdb/plugins/storage-lokijs';
-import {
- setFlutterRxDatabaseConnector,
- getLokijsAdapterFlutter
-} from 'rxdb/plugins/flutter';
-
-// do all database creation stuff in this method.
-async function createDB(databaseName) {
- // create the RxDatabase
- const db = await createRxDatabase({
- // the database.name is variable so we can change it on the flutter side
- name: databaseName,
- storage: getRxStorageLoki({
- adapter: getLokijsAdapterFlutter()
- }),
- multiInstance: false
- });
- await db.addCollections({
- heroes: {
- schema: {
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- name: {
- type: 'string',
- maxLength: 100
- },
- color: {
- type: 'string',
- maxLength: 30
- }
- },
- indexes: ['name'],
- required: ['id', 'name', 'color']
- }
- }
- });
- return db;
-}
-
-// start the connector so that flutter can communicate with the JavaScript process
-setFlutterRxDatabaseConnector(
- createDB
-);
-
Before you can use the JavaScript code, you have to bundle it into a single .js file. In this example we do that with webpack in a npm script here which bundles everything into the javascript/dist/index.js file.
-
To allow Flutter to access that file during runtime, add it to the assets inside of your pubspec.yaml:
-
flutter:
- assets:
- - javascript/dist/index.js
-
Also you need to install RxDB in your flutter part of the application. First you have to use the rxdb dart package as a flutter dependency. Currently the package is not published at the dart pub.dev. Instead you have to install it from the local filesystem inside of your RxDB npm installation.
-
# inside of pubspec.yaml
-dependencies:
- rxdb:
- path: path/to/your/node_modules/rxdb/src/plugins/flutter/dart
-
Afterwards you can import the rxdb library in your dart code and connect to the JavaScript process from there. For reference, check out the lib/main.dart file.
-
import 'package:rxdb/rxdb.dart';
-
-// start the javascript process and connect to the database
-RxDatabase database = await getRxDatabase("javascript/dist/index.js", databaseName);
-
-// get a collection
-RxCollection collection = database.getCollection('heroes');
-
-// insert a document
-RxDocument document = await collection.insert({
- "id": "zflutter-${DateTime.now()}",
- "name": nameController.text,
- "color": colorController.text
-});
-
-// create a query
-RxQuery<RxHeroDocType> query = RxDatabaseState.collection.find();
-
-// create list to store query results
-List<RxDocument<RxHeroDocType>> documents = [];
-
-// subscribe to a query
-query.$().listen((results) {
- setState(() {
- documents = results;
- });
-});
RxDB offers multiple storage options, known as RxStorage layers, to store data locally. These options include:
-
-
LokiJS RxStorage: LokiJS is an in-memory database that can be used as a storage layer for RxDB. It provides fast and efficient in-memory data management capabilities.
-
SQLite RxStorage: SQLite is a popular and widely used embedded database that offers robust storage capabilities. RxDB utilizes SQLite as a storage layer to persist data on the device.
-
Memory RxStorage: As the name suggests, Memory RxStorage stores data in memory. While this option does not provide persistence, it can be useful for temporary or cache-based data storage.
-By choosing the appropriate RxStorage layer based on the specific requirements of the application, developers can optimize performance and storage efficiency.
-
-
Synchronizing Data with RxDB between Clients and Servers
-
One of the key strengths of RxDB is its ability to synchronize data between multiple clients and servers seamlessly. Let's explore how this synchronization can be achieved.
RxDB's offline-first approach ensures that data can be accessed and modified even when there is no internet connection. Changes made offline are automatically synchronized with the server once a connection is reestablished. This ensures data consistency across all devices, providing a seamless user experience.
RxDB provides replication plugins that simplify the process of setting up data synchronization between clients and servers. These plugins offer various synchronization strategies, such as one-way replication, two-way replication, and conflict resolution mechanisms. By configuring the appropriate replication plugin, developers can easily establish real-time data synchronization in their Flutter applications.
Indexing is a technique used to optimize query performance by creating indexes on specific fields. RxDB allows developers to define indexes on document fields, improving the efficiency of queries and data retrieval.
To ensure data privacy and security, RxDB supports encryption of local data. By encrypting the data stored on the device, developers can protect sensitive information and prevent unauthorized access.
RxDB provides change streams, which emit events whenever data changes occur. By leveraging change streams, developers can implement custom event handling logic, such as updating the UI or triggering background processes, in response to specific data changes.
To minimize storage requirements and optimize performance, RxDB offers JSON key compression. This feature reduces the size of keys used in the database, resulting in more efficient storage and improved query performance.
RxDB offers a powerful and flexible database solution for Flutter applications. With its offline-first approach, real-time data synchronization, and reactive data handling capabilities, RxDB simplifies the development of feature-rich and scalable Flutter applications. By integrating RxDB into your Flutter projects, you can leverage its advanced features and techniques to build responsive and data-driven applications that provide an exceptional user experience.
-
note
You can find the source code for an example RxDB Flutter Application at the github repo
-
-
\ No newline at end of file
diff --git a/docs/articles/flutter-database.md b/docs/articles/flutter-database.md
deleted file mode 100644
index 1ebdf41b979..00000000000
--- a/docs/articles/flutter-database.md
+++ /dev/null
@@ -1,213 +0,0 @@
-# Supercharge Flutter Apps with the RxDB Database
-
-> Harness RxDB's reactive database to bring real-time, offline-first data storage and syncing to your next Flutter application.
-
-# RxDB as a Database in a Flutter Application
-
-In the world of mobile application development, Flutter has gained significant popularity due to its cross-platform capabilities and rich UI framework. When it comes to building feature-rich Flutter applications, the choice of a robust and efficient database is crucial. In this article, we will explore [RxDB](https://rxdb.info/) as a database solution for Flutter applications. We'll delve into the core features of RxDB, its benefits over other database options, and how to integrate it into a Flutter app.
-
-:::note
-You can find the source code for an example RxDB Flutter Application [at the github repo](https://github.com/pubkey/rxdb/tree/master/examples/flutter)
-:::
-
-
-
-
-
-
-
-### Overview of Flutter Mobile Applications
-Flutter is an open-source UI software development kit created by Google that allows developers to build high-performance [mobile](./mobile-database.md) applications for iOS and Android platforms using a single codebase. Flutter's framework provides a wide range of widgets and tools that enable developers to create visually appealing and responsive applications.
-
-
-
-
-
-### Importance of Databases in Flutter Applications
-Databases play a vital role in Flutter applications by providing a persistent and reliable storage solution for storing and retrieving data. Whether it's user profiles, app settings, or complex data structures, a database helps in efficiently managing and organizing the application's data. Choosing the right database for a Flutter application can significantly impact the performance, scalability, and user experience of the app.
-
-### Introducing RxDB as a Database Solution
-RxDB is a powerful NoSQL database solution that is designed to work seamlessly with JavaScript-based frameworks, such as Flutter. It stands for Reactive Database and offers a variety of features that make it an excellent choice for building Flutter applications. RxDB combines the simplicity of JavaScript's document-based database model with the reactive programming paradigm, enabling developers to build real-time and offline-first applications with ease.
-
-## Getting Started with RxDB
-To understand how RxDB can be utilized in a Flutter application, let's explore its core features and advantages.
-
-### What is RxDB?
-[RxDB](https://rxdb.info/) is a client-side database built on top of [IndexedDB](../rx-storage-indexeddb.md), which is a low-level [browser-based database](./browser-database.md) API. It provides a simple and intuitive API for performing CRUD operations (Create, Read, Update, Delete) on documents. RxDB's underlying architecture allows for efficient handling of data synchronization between multiple clients and servers.
-
-
-
-
-
-
-
-### Reactive Data Handling
-One of the key strengths of RxDB is its reactive data handling. It leverages the power of Observables, a concept from reactive programming, to automatically update the UI in response to data changes. With RxDB, developers can define queries and subscribe to their results, ensuring that the UI is always in sync with the database.
-
-### Offline-First Approach
-RxDB follows an offline-first approach, making it ideal for building Flutter applications that need to function even without an internet connection. It allows data to be stored locally and seamlessly synchronizes it with the server when a connection is available. This ensures that users can access and interact with their data regardless of network availability.
-
-### Data Replication
-Data replication is a critical aspect of building distributed applications. RxDB provides robust replication capabilities that enable synchronization of data between different clients and servers. With its replication plugins, RxDB simplifies the process of setting up real-time data synchronization, ensuring consistency across all connected devices.
-
-### Observable Queries
-RxDB introduces the concept of observable queries, which are queries that automatically update when the underlying data changes. This feature is particularly useful for keeping the UI up to date with the latest data. By subscribing to an observable query, developers can receive real-time updates and reflect them in the user interface without manual intervention.
-
-### RxDB vs. Other Flutter Database Options
-When considering database options for Flutter applications, developers often come across alternatives such as SQLite or LokiJS. While these databases have their merits, RxDB offers several advantages over them. RxDB's seamless integration with Flutter, its offline-first approach, reactive data handling, and built-in data replication make it a compelling choice for building feature-rich and scalable Flutter applications.
-
-## Using RxDB in a Flutter Application
-Now that we understand the core features of RxDB, let's explore how to integrate it into a Flutter application.
-
-## How RxDB can run in Flutter
-
-RxDB is written in TypeScript and compiled to JavaScript. To run it in a Flutter application, the `flutter_qjs` library is used to spawn a QuickJS JavaScript runtime. RxDB itself runs in that runtime and communicates with the flutter dart runtime. To store data persistent, the [LokiJS RxStorage](../rx-storage-lokijs.md) is used together with a custom storage adapter that persists the database inside of the `shared_preferences` data.
-
-To use RxDB, you have to create a compatible JavaScript file that creates your RxDatabase and starts some connectors which are used by Flutter to communicate with the JavaScript RxDB database via setFlutterRxDatabaseConnector().
-
-```javascript
-import {
- createRxDatabase
-} from 'rxdb';
-import {
- getRxStorageLoki
-} from 'rxdb/plugins/storage-lokijs';
-import {
- setFlutterRxDatabaseConnector,
- getLokijsAdapterFlutter
-} from 'rxdb/plugins/flutter';
-
-// do all database creation stuff in this method.
-async function createDB(databaseName) {
- // create the RxDatabase
- const db = await createRxDatabase({
- // the database.name is variable so we can change it on the flutter side
- name: databaseName,
- storage: getRxStorageLoki({
- adapter: getLokijsAdapterFlutter()
- }),
- multiInstance: false
- });
- await db.addCollections({
- heroes: {
- schema: {
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- name: {
- type: 'string',
- maxLength: 100
- },
- color: {
- type: 'string',
- maxLength: 30
- }
- },
- indexes: ['name'],
- required: ['id', 'name', 'color']
- }
- }
- });
- return db;
-}
-
-// start the connector so that flutter can communicate with the JavaScript process
-setFlutterRxDatabaseConnector(
- createDB
-);
-```
-
-Before you can use the JavaScript code, you have to bundle it into a single .js file. In this example we do that with webpack in a npm script here which bundles everything into the `javascript/dist/index.js` file.
-
-To allow Flutter to access that file during runtime, add it to the assets inside of your pubspec.yaml:
-
-```yaml
-flutter:
- assets:
- - javascript/dist/index.js
-```
-
-Also you need to install RxDB in your flutter part of the application. First you have to use the rxdb dart package as a flutter dependency. Currently the package is not published at the dart pub.dev. Instead you have to install it from the local filesystem inside of your RxDB npm installation.
-
-```yaml
-# inside of pubspec.yaml
-dependencies:
- rxdb:
- path: path/to/your/node_modules/rxdb/src/plugins/flutter/dart
-```
-
-Afterwards you can import the rxdb library in your dart code and connect to the JavaScript process from there. For reference, check out the lib/main.dart file.
-
-```dart
-import 'package:rxdb/rxdb.dart';
-
-// start the javascript process and connect to the database
-RxDatabase database = await getRxDatabase("javascript/dist/index.js", databaseName);
-
-// get a collection
-RxCollection collection = database.getCollection('heroes');
-
-// insert a document
-RxDocument document = await collection.insert({
- "id": "zflutter-${DateTime.now()}",
- "name": nameController.text,
- "color": colorController.text
-});
-
-// create a query
-RxQuery query = RxDatabaseState.collection.find();
-
-// create list to store query results
-List> documents = [];
-
-// subscribe to a query
-query.$().listen((results) {
- setState(() {
- documents = results;
- });
-});
-```
-
-### Different RxStorage layers for RxDB
-RxDB offers multiple storage options, known as RxStorage layers, to store data locally. These options include:
-
-- [LokiJS RxStorage](../rx-storage-lokijs.md): LokiJS is an in-memory database that can be used as a [storage](./browser-storage.md) layer for RxDB. It provides fast and efficient in-memory data management capabilities.
-- [SQLite RxStorage](../rx-storage-sqlite.md): SQLite is a popular and widely used [embedded database](./embedded-database.md) that offers robust storage capabilities. RxDB utilizes SQLite as a storage layer to persist data on the device.
-- [Memory RxStorage](../rx-storage-memory.md): As the name suggests, Memory RxStorage stores data [in memory](./in-memory-nosql-database.md). While this option does not provide persistence, it can be useful for temporary or cache-based data storage.
-By choosing the appropriate RxStorage layer based on the specific requirements of the application, developers can optimize performance and storage efficiency.
-
-## Synchronizing Data with RxDB between Clients and Servers
-One of the key strengths of RxDB is its ability to synchronize data between multiple clients and servers seamlessly. Let's explore how this synchronization can be achieved.
-
-### Offline-First Approach
-RxDB's offline-first approach ensures that data can be accessed and modified even when there is no internet connection. Changes made offline are automatically synchronized with the server once a connection is reestablished. This ensures data consistency across all devices, providing a seamless user experience.
-
-### RxDB Replication Plugins
-RxDB provides replication plugins that simplify the process of setting up data [synchronization between clients and servers](../replication.md). These plugins offer various synchronization strategies, such as one-way replication, two-way replication, and conflict resolution mechanisms. By configuring the appropriate replication plugin, developers can easily establish real-time data synchronization in their Flutter applications.
-
-## Advanced RxDB Features and Techniques
-RxDB offers a range of advanced features and techniques that enhance its functionality and performance. Let's explore a few of these features:
-
-### Indexing and Performance Optimization
-Indexing is a technique used to optimize query performance by creating indexes on specific fields. RxDB allows developers to define indexes on document fields, improving the efficiency of queries and data retrieval.
-
-### Encryption of Local Data
-To ensure data privacy and security, RxDB supports [encryption of local data](../encryption.md). By encrypting the data stored on the device, developers can protect sensitive information and prevent unauthorized access.
-
-### Change Streams and Event Handling
-RxDB provides change streams, which emit events whenever data changes occur. By leveraging change streams, developers can implement custom event handling logic, such as updating the UI or triggering background processes, in response to specific data changes.
-
-### JSON Key Compression
-To minimize storage requirements and optimize performance, RxDB offers [JSON key compression](../key-compression.md). This feature reduces the size of keys used in the database, resulting in more efficient storage and improved query performance.
-
-## Conclusion
-RxDB offers a powerful and flexible database solution for Flutter applications. With its offline-first approach, real-time data synchronization, and reactive data handling capabilities, RxDB simplifies the development of feature-rich and scalable Flutter applications. By integrating RxDB into your Flutter projects, you can leverage its advanced features and techniques to build responsive and data-driven applications that provide an exceptional user experience.
-
-:::note
-You can find the source code for an example RxDB Flutter Application [at the github repo](https://github.com/pubkey/rxdb/tree/master/examples/flutter)
-:::
diff --git a/docs/articles/frontend-database.html b/docs/articles/frontend-database.html
deleted file mode 100644
index 3cf213783d6..00000000000
--- a/docs/articles/frontend-database.html
+++ /dev/null
@@ -1,119 +0,0 @@
-
-
-
-
-
-RxDB - The Ultimate JS Frontend Database | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
RxDB JavaScript Frontend Database: Efficient Data Storage in Frontend Applications
-
In modern web development, managing data on the front end has become increasingly important. Storing data in the frontend offers numerous advantages, such as offline accessibility, caching, faster application startup, and improved state management. Traditional SQL databases, although widely used on the server-side, are not always the best fit for frontend applications. This is where RxDB, a frontend JavaScript database, emerges as a powerful solution. In this article, we will explore why storing data in the frontend is beneficial, the limitations of SQL databases in the frontend, and how RxDB addresses these challenges to become an excellent choice for frontend data storage.
One compelling reason to store data in the frontend is to enable offline accessibility. By leveraging a frontend database, applications can cache essential data locally, allowing users to continue using the application even when an internet connection is unavailable. This feature is particularly useful for mobile applications or web apps with limited or intermittent connectivity.
Frontend databases also serve as efficient caching mechanisms. By storing frequently accessed data locally, applications can minimize network requests and reduce latency, resulting in faster and more responsive user experiences. Caching is particularly beneficial for applications that heavily rely on remote data or perform computationally intensive operations.
Storing data in the frontend decreases the initial application start time because the data is already present locally. By eliminating the need to fetch data from a server during startup, applications can quickly render the UI and provide users with an immediate interactive experience. This is especially advantageous for applications with large datasets or complex data retrieval processes.
Security is a crucial aspect of data storage. With a front end database, developers can encrypt sensitive local data, such as user credentials or personal information, using encryption algorithms. This ensures that even if the device is compromised, the data remains securely stored and protected.
Frontend databases provide an alternative to traditional state management libraries like Redux or NgRx. By utilizing a local database, developers can store and manage application state directly in the frontend, eliminating the need for additional libraries. This approach simplifies the codebase, reduces complexity, and provides a more straightforward data flow within the application.
Frontend databases enable low-latency queries that run entirely on the client's device. Instead of relying on server round-trips for each query, the database executes queries locally, resulting in faster response times. This is particularly beneficial for applications that require real-time updates or frequent data retrieval.
Realtime applications often require immediate updates based on data changes. By storing data locally and utilizing a frontend database, developers can build realtime applications more easily. The database can observe data changes and automatically update the UI, providing a seamless and responsive user experience.
Frontend databases, including RxDB, are designed to integrate seamlessly with popular JavaScript frameworks such as Angular, React.js, Vue.js, and Svelte. These databases offer well-defined APIs and support that align with the specific requirements of these frameworks, enabling developers to leverage the full potential of the frontend database within their preferred development environment.
Replicating database state between the frontend and backend can be challenging, especially when dealing with complex REST routes. Frontend databases, however, provide simple mechanisms for replicating database state. They offer intuitive replication algorithms that facilitate data synchronization between the frontend and backend, reducing the complexity and potential pitfalls associated with complex REST-based replication.
Frontend databases offer improved scalability compared to traditional SQL databases. By leveraging the computational capabilities of client devices, the burden on server resources is reduced. Queries and operations are performed locally, minimizing the need for server round-trips and enabling applications to scale more efficiently.
-
Why SQL databases are not a good fit for the front end of an application
-
While SQL databases excel in server-side scenarios, they pose limitations when used on the frontend. Here are some reasons why SQL databases are not well-suited for frontend applications:
SQL databases typically rely on a push/pull model, where the server pushes data to the client upon request. This approach is not inherently reactive, as it requires explicit requests for data updates. In contrast, frontend applications often require reactive data flows, where changes in data trigger automatic updates in the UI. Frontend databases, like RxDB, provide reactive capabilities that seamlessly integrate with the dynamic nature of frontend development.
SQL databases designed for server-side usage tend to have larger build sizes and initialization times, making them less efficient for browser-based applications. Frontend databases, on the other hand, directly leverage browser APIs like IndexedDB, OPFS, and WebWorker, resulting in leaner builds and faster initialization times. Often the queries are such fast, that it is not even necessary to implement a loading spinner.
Server-side SQL databases typically come with a significant build size, which can be impractical for browser applications where code size optimization is crucial. Frontend databases, on the other hand, are specifically designed to operate within the constraints of browser environments, ensuring efficient resource utilization and smaller build sizes.
-
For example the SQLite Webassembly file alone has a size of over 0.8 Megabyte with an additional 0.2 Megabyte in JavaScript code for connection.
RxDB is a powerful frontend JavaScript database that addresses the limitations of SQL databases and provides an optimal solution for frontend data storage. Let's explore why RxDB is an excellent fit for frontend applications:
-
Made in JavaScript, optimized for JavaScript applications
-
RxDB is designed and optimized for JavaScript applications. Built using JavaScript itself, RxDB offers seamless integration with JavaScript frameworks and libraries, allowing developers to leverage their existing JavaScript knowledge and skills.
RxDB adopts a NoSQL approach, using JSON documents as its primary data structure. This aligns well with the JavaScript ecosystem, as JavaScript natively works with JSON objects. By using NoSQL documents, RxDB provides a more natural and intuitive data model for UI-centric applications.
TypeScript has become increasingly popular for building frontend applications. RxDB provides excellent TypeScript support, allowing developers to leverage static typing and benefit from enhanced code quality and tooling. This is particularly advantageous when compared to SQL databases, which often have limited TypeScript support.
RxDB introduces the concept of observable queries, powered by RxJS. Observable queries automatically update the UI whenever there are changes in the underlying data. This reactive approach eliminates the need for manual UI updates and ensures that the frontend remains synchronized with the database state.
-
-
Optimized observed queries with the EventReduce Algorithm
-
RxDB optimizes observed queries with its EventReduce Algorithm. This algorithm intelligently reduces redundant events and ensures that UI updates are performed efficiently. By minimizing unnecessary re-renders, RxDB significantly improves performance and responsiveness in frontend applications.
RxDB supports observable document fields, enabling developers to track changes at a granular level within documents. By observing specific fields, developers can reactively update the UI when those fields change, ensuring a responsive and synchronized frontend interface.
-
myDocument.firstName$.subscribe(newName => console.log('name is: ' + newName));
RxDB provides the option to store documents in a compressed format, reducing storage requirements and improving overall database performance. Compressed storage offers benefits such as reduced disk space usage, faster data read/write operations, and improved network transfer speeds, making it an essential feature for efficient frontend data storage.
RxDB offers built-in multi-tab support, allowing data synchronization and state management across multiple browser tabs. This feature ensures consistent data access and synchronization, enabling users to work seamlessly across different tabs without conflicts or data inconsistencies.
-
-
Replication Algorithm can be made compatible with any backend
-
RxDB's realtime replication algorithm is designed to be flexible and compatible with various backend systems. Whether you're using your own servers, Firebase, CouchDB, NATS, WebSocket, or any other backend, RxDB can be seamlessly integrated and synchronized with the backend system of your choice.
RxDB provides a flexible storage layer that enables code reuse across different platforms. Whether you're building applications with Electron.js, React Native, hybrid apps using Capacitor.js, or traditional web browsers, RxDB allows you to reuse the same codebase and leverage the power of a frontend database across different environments.
-
Handling schema changes in distributed environments
-
In distributed environments where data is stored on multiple client devices, handling schema changes can be challenging. RxDB tackles this challenge by providing robust mechanisms for handling schema changes. It ensures that schema updates propagate smoothly across devices, maintaining data integrity and enabling seamless schema evolution.
To further explore RxDB and get started with using it in your frontend applications, consider the following resources:
-
-
RxDB Quickstart: A step-by-step guide to quickly set up RxDB in your project and start leveraging its features.
-
RxDB GitHub Repository: The official repository for RxDB, where you can find the code, examples, and community support.
-
-
By adopting RxDB as your frontend database, you can unlock the full potential of frontend data storage and empower your applications with offline accessibility, caching, improved performance, and seamless data synchronization. RxDB's JavaScript-centric approach and powerful features make it an ideal choice for frontend developers seeking efficient and scalable data storage solutions.
-
-
\ No newline at end of file
diff --git a/docs/articles/frontend-database.md b/docs/articles/frontend-database.md
deleted file mode 100644
index f194abfb81d..00000000000
--- a/docs/articles/frontend-database.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# RxDB - The Ultimate JS Frontend Database
-
-> Discover how RxDB, a powerful JavaScript frontend database, boosts offline access, caching, and real-time updates to supercharge your web apps.
-
-# RxDB JavaScript Frontend Database: Efficient Data Storage in Frontend Applications
-In modern web development, managing data on the front end has become increasingly important. Storing data in the frontend offers numerous advantages, such as offline accessibility, caching, faster application startup, and improved state management. Traditional SQL databases, although widely used on the server-side, are not always the best fit for frontend applications. This is where [RxDB](https://rxdb.info/), a frontend JavaScript database, emerges as a powerful solution. In this article, we will explore why storing data in the frontend is beneficial, the limitations of SQL databases in the frontend, and how [RxDB](https://rxdb.info/) addresses these challenges to become an excellent choice for frontend data storage.
-
-
-
-
-
-
-
-## Why you might want to store data in the frontend
-
-### Offline accessibility
-One compelling reason to store data in the frontend is to enable [offline accessibility](../offline-first.md). By leveraging a frontend database, applications can cache essential data locally, allowing users to continue using the application even when an internet connection is unavailable. This feature is particularly useful for [mobile](./mobile-database.md) applications or web apps with limited or intermittent connectivity.
-
-### Caching
-Frontend databases also serve as efficient caching mechanisms. By storing frequently accessed data locally, applications can minimize network requests and reduce latency, resulting in faster and more responsive user experiences. Caching is particularly beneficial for applications that heavily rely on remote data or perform computationally intensive operations.
-
-### Decreased initial application start time
-Storing data in the frontend decreases the initial application start time because the data is already present locally. By eliminating the need to fetch data from a server during startup, applications can quickly render the UI and provide users with an immediate interactive experience. This is especially advantageous for applications with large datasets or complex data retrieval processes.
-
-### Password encryption for local data
-Security is a crucial aspect of data storage. With a front end database, developers can [encrypt](../encryption.md) sensitive local data, such as user credentials or personal information, using encryption algorithms. This ensures that even if the device is compromised, the data remains securely stored and protected.
-
-### Local database for state management
-Frontend databases provide an alternative to traditional state management libraries like Redux or NgRx. By utilizing a local database, developers can store and manage application state directly in the frontend, eliminating the need for additional libraries. This approach simplifies the codebase, reduces complexity, and provides a more straightforward data flow within the application.
-
-### Low-latency local queries
-Frontend databases enable low-latency queries that run entirely on the client's device. Instead of relying on server round-trips for each query, the database executes queries locally, resulting in faster response times. This is particularly beneficial for applications that require real-time updates or frequent data retrieval.
-
-
-
-### Building realtime applications with local data
-Realtime applications often require immediate updates based on data changes. By storing data locally and utilizing a frontend database, developers can build [realtime applications](./realtime-database.md) more easily. The database can observe data changes and automatically update the UI, providing a seamless and responsive user experience.
-
-### Easier integration with JavaScript frameworks
-Frontend databases, including RxDB, are designed to integrate seamlessly with popular JavaScript frameworks such as [Angular](./angular-database.md), [React.js](./react-database.md), [Vue.js](./vue-database.md), and Svelte. These databases offer well-defined APIs and support that align with the specific requirements of these frameworks, enabling developers to leverage the full potential of the frontend database within their preferred development environment.
-
-### Simplified replication of database state
-Replicating database state between the frontend and backend can be challenging, especially when dealing with complex REST routes. Frontend databases, however, provide simple mechanisms for replicating database state. They offer intuitive replication algorithms that facilitate data synchronization between the frontend and backend, reducing the complexity and potential pitfalls associated with complex REST-based replication.
-
-### Improved scalability
-Frontend databases offer improved scalability compared to traditional SQL databases. By leveraging the computational capabilities of client devices, the burden on server resources is reduced. Queries and operations are performed locally, minimizing the need for server round-trips and enabling applications to scale more efficiently.
-
-## Why SQL databases are not a good fit for the front end of an application
-While SQL databases excel in server-side scenarios, they pose limitations when used on the frontend. Here are some reasons why SQL databases are not well-suited for frontend applications:
-
-### Push/Pull based vs. reactive
-SQL databases typically rely on a push/pull model, where the server pushes data to the client upon request. This approach is not inherently reactive, as it requires explicit requests for data updates. In contrast, frontend applications often require reactive data flows, where changes in data trigger automatic updates in the UI. Frontend databases, like [RxDB](https://rxdb.info/), provide reactive capabilities that seamlessly integrate with the dynamic nature of frontend development.
-
-### Initialization time and performance
-SQL databases designed for server-side usage tend to have larger build sizes and initialization times, making them less efficient for [browser-based](./browser-database.md) applications. Frontend databases, on the other hand, directly leverage browser APIs like [IndexedDB](../rx-storage-indexeddb.md), [OPFS](../rx-storage-opfs.md), and [WebWorker](../rx-storage-worker.md), resulting in leaner builds and faster initialization times. Often the queries are such fast, that it is not even necessary to implement a loading spinner.
-
-
-
-### Build size considerations
-Server-side SQL databases typically come with a significant build size, which can be impractical for browser applications where code size optimization is crucial. Frontend databases, on the other hand, are specifically designed to operate within the constraints of browser environments, ensuring efficient resource utilization and smaller build sizes.
-
-For example the SQLite Webassembly file alone has a size of over 0.8 Megabyte with an additional 0.2 Megabyte in JavaScript code for connection.
-
-## Why RxDB is a good fit for the frontend
-RxDB is a powerful frontend JavaScript database that addresses the limitations of SQL databases and provides an optimal solution for frontend [data storage](./browser-storage.md). Let's explore why RxDB is an excellent fit for frontend applications:
-
-### Made in JavaScript, optimized for JavaScript applications
-RxDB is designed and optimized for JavaScript applications. Built using JavaScript itself, RxDB offers seamless integration with JavaScript frameworks and libraries, allowing developers to leverage their existing JavaScript knowledge and skills.
-
-### NoSQL (JSON) documents for UIs
-RxDB adopts a [NoSQL approach](./in-memory-nosql-database.md), using [JSON documents as its primary data structure](./json-database.md). This aligns well with the JavaScript ecosystem, as JavaScript natively works with JSON objects. By using NoSQL documents, RxDB provides a more natural and intuitive data model for UI-centric applications.
-
-
-
-### Better TypeScript support compared to SQL
-TypeScript has become increasingly popular for building frontend applications. RxDB provides excellent [TypeScript support](../tutorials/typescript.md), allowing developers to leverage static typing and benefit from enhanced code quality and tooling. This is particularly advantageous when compared to SQL databases, which often have limited TypeScript support.
-
-### Observable Queries for automatic UI updates
-RxDB introduces the concept of observable queries, powered by RxJS. Observable queries automatically update the UI whenever there are changes in the underlying data. This reactive approach eliminates the need for manual UI updates and ensures that the frontend remains synchronized with the database state.
-
-
-
-### Optimized observed queries with the EventReduce Algorithm
-RxDB optimizes observed queries with its EventReduce Algorithm. This algorithm intelligently reduces redundant events and ensures that UI updates are performed efficiently. By minimizing unnecessary re-renders, RxDB significantly improves performance and responsiveness in frontend applications.
-
-```typescript
-const query = myCollection.find({
- selector: {
- age: {
- $gt: 21
- }
- }
-});
-const querySub = query.$.subscribe(results => {
- console.log('got results: ' + results.length);
-});
-```
-
-### Observable document fields
-RxDB supports observable document fields, enabling developers to track changes at a granular level within documents. By observing specific fields, developers can reactively update the UI when those fields change, ensuring a responsive and synchronized frontend interface.
-
-```typescript
-myDocument.firstName$.subscribe(newName => console.log('name is: ' + newName));
-```
-
-### Storing Documents Compressed
-RxDB provides the option to store documents in a [compressed format](../key-compression.md), reducing storage requirements and improving overall database performance. Compressed storage offers benefits such as reduced disk space usage, faster data read/write operations, and improved network transfer speeds, making it an essential feature for efficient frontend data storage.
-
-### Built-in Multi-tab support
-RxDB offers built-in multi-tab support, allowing data synchronization and state management across multiple browser tabs. This feature ensures consistent data access and synchronization, enabling users to work seamlessly across different tabs without conflicts or data inconsistencies.
-
-
-
-### Replication Algorithm can be made compatible with any backend
-RxDB's [realtime replication algorithm](../replication.md) is designed to be flexible and compatible with various backend systems. Whether you're using your own servers, [Firebase](../replication-firestore.md), [CouchDB](../replication-couchdb.md), [NATS](../replication-nats.md), [WebSocket](../replication-websocket.md), or any other backend, RxDB can be seamlessly integrated and synchronized with the backend system of your choice.
-
-
-
-### Flexible storage layer for code reuse
-RxDB provides a [flexible storage layer](../rx-storage.md) that enables code reuse across different platforms. Whether you're building applications with [Electron.js](../electron-database.md), [React Native](../react-native-database.md), hybrid apps using [Capacitor.js](../capacitor-database.md), or traditional web browsers, RxDB allows you to reuse the same codebase and leverage the power of a frontend database across different environments.
-
-### Handling schema changes in distributed environments
-In distributed environments where data is stored on multiple client devices, handling schema changes can be challenging. RxDB tackles this challenge by providing robust mechanisms for [handling schema changes](../migration-schema.md). It ensures that schema updates propagate smoothly across devices, maintaining data integrity and enabling seamless schema evolution.
-
-## Follow Up
-To further explore RxDB and get started with using it in your frontend applications, consider the following resources:
-
-- [RxDB Quickstart](../quickstart.md): A step-by-step guide to quickly set up RxDB in your project and start leveraging its features.
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): The official repository for RxDB, where you can find the code, examples, and community support.
-
-By adopting [RxDB](https://rxdb.info/) as your frontend database, you can unlock the full potential of frontend data storage and empower your applications with offline accessibility, caching, improved performance, and seamless data synchronization. RxDB's JavaScript-centric approach and powerful features make it an ideal choice for frontend developers seeking efficient and scalable data storage solutions.
diff --git a/docs/articles/ideas.md b/docs/articles/ideas.md
deleted file mode 100644
index bccca3271b0..00000000000
--- a/docs/articles/ideas.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# ideas for articles
-
-> - storing and searching through 1mio emails in a browser database
-- Finding the optimal way to shorten vector embeddings
-- Performance and quality of vector comparison functions (euclideanDistance etc)
-- performance and quality of vector indexing methods
-- What is new in IndexedDB 3.0
-- how progressive syncing beats client-server architecture
-
-# ideas for articles
-
-- storing and searching through 1mio emails in a browser database
-- Finding the optimal way to shorten vector embeddings
-- Performance and quality of vector comparison functions (euclideanDistance etc)
-- performance and quality of vector indexing methods
-- What is new in IndexedDB 3.0
-- how progressive syncing beats client-server architecture
-
-## Seo keywords:
-
-X- "optimistic ui"
-X- "local database" (rddt done)
-X- "react-native encryption"
-X- "vue database" (rddt done)
-X- "jquery database"
-X- "vue indexeddb"
-X- "firebase realtime database alternative" (rddt done)
-X- "firestore alternative" (rddt done)
-X- "ionic storage" (rddt done)
-X- "local database"
-X- "offline database"
-X- "zero local first"
-X- "webrtc p2p" - 390 http://localhost:3000/replication-webrtc.html
-
-X- "indexeddb storage limit" - 590 https://rxdb.info/articles/indexeddb-max-storage-limit.html
-X- "indexeddb size limit" - 260 https://rxdb.info/articles/indexeddb-max-storage-limit.html
-X- "indexeddb max size" - 590 https://rxdb.info/articles/indexeddb-max-storage-limit.html
-X- "indexeddb limits" - 170 https://rxdb.info/articles/indexeddb-max-storage-limit.html
-
-X- "json based database"
-X- "json vs database"
-X- "reactjs storage"
-
-## Seo
-
-- "supabase alternative"
-- "store local storage"
-- "react localstorage"
-- "react-native storage"
-- "supabase offline" - 260
-- "store array in localstorage", "localStorage array of objects"
-- "real time web apps" - 170
-- "reactive database" - 210
-- "electron sqlite"
-- "in browser database" - 90
-- "offline first app" - 260
-- "react native sql" - 110
-- "sqlite electron"
-- "localstorage vs indexeddb"
-- "react native nosql database" - 30
-- "indexeddb library" - 260
-- "indexeddb encryption" - 90
-- "client side database" - 140
-- "webtransport vs websocket"
-- "local first development" - 210
-- "local storage examples"
-- "local vector database" - 590
-- "mobile app database" - 590
-- "web based database"
-- "livequery" - 210
-- "expo database" - 390
-- "database sync" - 8100
-- "p2p database" - 170
-- "reactive app" - 260
-- "offline web app" - 320
-- "offline sync" - 320
-- "react native encrypted storage" - 1000
-- "firestore vs firebase" - 1300
-- "ionic alternatives" - 480
-- "react native backend" - 720
-- "react native alternative" - 1000
-- "react native sqlite" - 1900
-- "flutter vs react native" - 5400
-- "react native redux" - 3600
-- "redux alternative" - 1300
-- "Awesome local first" - 10
-- "tauri database" - 170
-
-- "sqlite javascript" - 2900
-- "sqlite typescript" - 260
-
-- "sync engine" - 390
-- "indexeddb alternative" - 70
-
-## Non Seo
-
-- "Local-First Partial Sync with RxDB"
-- "why the indexeddb API is almost perfekt"
-- "how to do auth with RxDB"
-- "Where to store that JWT token?"
diff --git a/docs/articles/ideas/index.html b/docs/articles/ideas/index.html
deleted file mode 100644
index 3e353d4c23e..00000000000
--- a/docs/articles/ideas/index.html
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
-
-
-
-ideas for articles | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
RxDB as In-memory NoSQL Database: Empowering Real-Time Applications
-
Real-time applications have become increasingly popular in today's digital landscape. From instant messaging to collaborative editing tools, the demand for responsive and interactive software is on the rise. To meet these requirements, developers need powerful and efficient database solutions that can handle large amounts of data in real-time. RxDB, an javascript NoSQL database, is revolutionizing the way developers build and scale their applications by offering exceptional speed, flexibility, and scalability.
One of the key advantages of using RxDB as an in-memory NoSQL database is its ability to leverage in-memory storage for faster database operations. By storing data directly in memory, database operations can be performed significantly faster compared to traditional disk-based databases. This is especially important for real-time applications where every millisecond counts. With RxDB, developers can achieve near-instantaneous data access and manipulation, enabling highly responsive user experiences.
-
Additionally, RxDB eliminates disk I/O bottlenecks that are typically associated with traditional databases. In traditional databases, disk reads and writes can become a bottleneck as the amount of data grows. In contrast, an in-memory database like RxDB keeps the entire dataset in RAM, eliminating disk access overhead. This makes it an excellent choice for applications dealing with real-time analytics, high-throughput data processing, and caching.
While RxDB offers an in-memory storage adapter, it also offers persistence storages. Adapters such as IndexedDB, SQLite, and OPFS enable developers to persist data locally in the browser, making applications accessible even when offline. This hybrid approach combines the benefits of in-memory performance with data durability, providing the best of both worlds. Developers can choose the adapter that best suits their needs, balancing the speed of in-memory storage with the long-term data persistence required for certain applications.
-
import {
- createRxDatabase
-} from 'rxdb';
-import {
- getRxStorageMemory
-} from 'rxdb/plugins/storage-memory';
-
-const db = await createRxDatabase({
- name: 'exampledb',
- storage: getRxStorageMemory()
-});
-
Also the memory mapped RxStorage exists as a wrapper around any other RxStorage. The wrapper creates an in-memory storage that is used for query and write operations. This memory instance is replicated with the underlying storage for persistence. The main reason to use this is to improve initial page load and query/write times. This is mostly useful in browser based applications.
RxDB's capabilities make it well-suited for various real-time applications. Some notable use cases include:
-
-
-
Chat Applications and Real-Time Messaging: RxDB's in-memory performance and real-time synchronization capabilities make it an excellent choice for building chat applications and real-time messaging systems. Developers can ensure that messages are delivered and synchronized across multiple clients in real-time, providing a seamless and responsive chat experience.
-
-
-
Collaborative Document Editors: RxDB's ability to handle data streams and propagate changes in real-time makes it ideal for collaborative document editing. Multiple users can simultaneously edit a document, and their changes are instantly synchronized, allowing for real-time collaboration and ensuring that everyone has the most up-to-date version of the document.
-
-
-
Real-Time Analytics Dashboards: RxDB's speed and scalability make it a valuable tool for real-time analytics dashboards. It can handle high volumes of data and perform complex analytics operations in real-time, providing instant insights and visualizations to users.
-
-
-
In conclusion, RxDB serves as a powerful in-memory NoSQL database that empowers developers to build real-time applications with exceptional speed, flexibility, and scalability. Its ability to leverage in-memory storage, eliminate disk I/O bottlenecks, and provide persistence options make it an attractive choice for a wide range of real-time use cases. Whether it's chat applications, collaborative document editors, or real-time analytics dashboards, RxDB provides the foundation for building responsive and interactive software that meets the demands of today's users.
-
-
\ No newline at end of file
diff --git a/docs/articles/in-memory-nosql-database.md b/docs/articles/in-memory-nosql-database.md
deleted file mode 100644
index 4ef9076b1fa..00000000000
--- a/docs/articles/in-memory-nosql-database.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# RxDB In-Memory NoSQL - Supercharge Real-Time Apps
-
-> Discover how RxDB's in-memory NoSQL engine delivers blazing speed for real-time apps, ensuring responsive experiences and seamless data sync.
-
-# RxDB as In-memory NoSQL Database: Empowering Real-Time Applications
-
-Real-time applications have become increasingly popular in today's digital landscape. From instant messaging to collaborative editing tools, the demand for responsive and interactive software is on the rise. To meet these requirements, developers need powerful and efficient database solutions that can handle large amounts of data in real-time. [RxDB](https://rxdb.info/), an javascript NoSQL database, is revolutionizing the way developers build and scale their applications by offering exceptional speed, flexibility, and scalability.
-
-
-
-
-
-
-
-## Speed and Performance Benefits
-One of the key advantages of using RxDB as an in-memory NoSQL database is its ability to leverage in-memory storage for faster database operations. By storing data directly in memory, database operations can be performed significantly faster compared to traditional disk-based databases. This is especially important for real-time applications where every millisecond counts. With RxDB, developers can achieve near-instantaneous data access and manipulation, enabling highly responsive user experiences.
-
-Additionally, RxDB eliminates disk I/O bottlenecks that are typically associated with traditional databases. In traditional databases, disk reads and writes can become a bottleneck as the amount of data grows. In contrast, an in-memory database like RxDB keeps the entire dataset in RAM, eliminating disk access overhead. This makes it an excellent choice for applications dealing with real-time analytics, high-throughput data processing, and caching.
-
-## Persistence Options
-While RxDB offers an [in-memory](../rx-storage-memory.md) storage adapter, it also offers [persistence storages](../rx-storage.md). Adapters such as [IndexedDB](../rx-storage-indexeddb.md), [SQLite](../rx-storage-sqlite.md), and [OPFS](../rx-storage-opfs.md) enable developers to persist data locally in the browser, making applications accessible even when offline. This hybrid approach combines the benefits of in-memory performance with data durability, providing the best of both worlds. Developers can choose the adapter that best suits their needs, balancing the speed of in-memory storage with the long-term data persistence required for certain applications.
-
-```javascript
-import {
- createRxDatabase
-} from 'rxdb';
-import {
- getRxStorageMemory
-} from 'rxdb/plugins/storage-memory';
-
-const db = await createRxDatabase({
- name: 'exampledb',
- storage: getRxStorageMemory()
-});
-```
-
-Also the [memory mapped RxStorage](../rx-storage-memory-mapped.md) exists as a wrapper around any other RxStorage. The wrapper creates an in-memory storage that is used for query and write operations. This memory instance is replicated with the underlying storage for persistence. The main reason to use this is to improve initial page load and query/write times. This is mostly useful in browser based applications.
-
-## Use Cases for RxDB
-RxDB's capabilities make it well-suited for various real-time applications. Some notable use cases include:
-
-- Chat Applications and Real-Time Messaging: RxDB's in-memory performance and real-time synchronization capabilities make it an excellent choice for building chat applications and real-time messaging systems. Developers can ensure that messages are delivered and synchronized across multiple clients in real-time, providing a seamless and responsive chat experience.
-
-- Collaborative Document Editors: RxDB's ability to handle data streams and propagate changes in real-time makes it ideal for collaborative document editing. Multiple users can simultaneously edit a document, and their changes are instantly synchronized, allowing for real-time collaboration and ensuring that everyone has the most up-to-date version of the document.
-
-- Real-Time Analytics Dashboards: RxDB's speed and scalability make it a valuable tool for real-time analytics dashboards. It can handle high volumes of data and perform complex analytics operations in real-time, providing instant insights and visualizations to users.
-
-In conclusion, RxDB serves as a powerful in-memory NoSQL database that empowers developers to build real-time applications with exceptional speed, flexibility, and scalability. Its ability to leverage in-memory storage, eliminate disk I/O bottlenecks, and provide persistence options make it an attractive choice for a wide range of real-time use cases. Whether it's chat applications, collaborative document editors, or real-time analytics dashboards, RxDB provides the foundation for building responsive and interactive software that meets the demands of today's users.
diff --git a/docs/articles/indexeddb-max-storage-limit.html b/docs/articles/indexeddb-max-storage-limit.html
deleted file mode 100644
index b034853766b..00000000000
--- a/docs/articles/indexeddb-max-storage-limit.html
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
-
-
-IndexedDB Max Storage Size Limit - Detailed Best Practices | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
IndexedDB is widely known as the primary browser-based storage API for large client-side data, particularly valuable for modern offline-first applications. These apps aim to keep everything functional and interactive even without an internet connection, which naturally demands substantial local storage. However, IndexedDB has various size limits depending on the browser, disk space, and user settings. Being aware of these constraints is crucial so you can avoid quota errors and deliver a seamless user experience without unexpected data loss.
-
Offline-first apps have grown in popularity because they provide immediate feedback, zero-latency interactions, and resilience in poor network conditions. Storing big data sets, or even entire data models, in IndexedDB has become far more common than in the era of small localStorage or cookie usage. But all this local data is subject to quotas, and that’s exactly what this guide will help you understand and manage.
Browsers need a way to curb runaway disk usage and safeguard user resources. This is accomplished through quota management policies, which can vary among Chrome, Firefox, Safari, Edge, and others. Some browsers use a percentage of your total disk space, while others rely on a fixed maximum or dynamic approach per origin. These policies are designed to prevent malicious or poorly optimized web pages from consuming an unreasonable amount of user storage.
-
Chrome (and Chromium-based browsers) typically allow you to use a percentage of the user’s free disk space, whereas Firefox historically prompts users to allow more than 5 MB in mobile or 50 MB in desktop. Safari often sets tighter maximum caps, especially on iOS devices. Edge aligns closely with Chrome’s rules but can also include enterprise or corporate policy overrides. Understanding these default or dynamic limits prepares you to plan your app’s storage needs appropriately.
IndexedDB size quotas differ significantly across browsers and platforms. While there isn’t a universal rule, the following table summarizes approximate limits and any notes or caveats you should be aware of:
-
Browser
Approx. Limit
Notes
Chrome/Chromium
Up to ~80% of free disk, per origin cap
Often cited as 60 GB on a 100 GB drive. Shared pool approach. Quota usage can prompt partial or extended user approvals.
Firefox
~2 GB (desktop) or ~5 MB initial for mobile
Older versions asked permission at 50 MB for desktop. Ephemeral/incognito sessions may require repeated user prompts.
Safari (iOS)
~1 GB per origin (variable)
Historically stricter. iOS devices limit quotas further. Behavior can differ between iOS Safari versions or iPadOS.
Edge
Similar to Chrome’s 80% of free space
Can be influenced by Windows enterprise policies. Generally aligned with Chromium approach.
iOS Safari
Typically 1 GB, can be less on older iOS
Early iOS versions were known for more aggressive quotas and data eviction on low space.
Android Chrome
Similar to desktop Chrome
May exhibit warnings in especially low-storage devices. The same 80% free space logic generally applies.
-
Historically, these limits have evolved. For instance, older Firefox versions included dom.indexedDB.warningQuota, showing a 50 MB prompt on desktop or a 5 MB prompt on mobile—many developers wrote about these notifications on Stack Overflow. Since around 2015, Firefox has changed its quota approach significantly. Likewise, Safari used to limit data more aggressively on older iOS versions. Some older tutorials suggest comparing IndexedDB to localStorage, but modern browsers allow far larger and more flexible storage with IndexedDB than the old localStorage or cookie-based setups.
To assess where your app stands relative to these storage limits, you can use the Storage Estimation API. The snippet below shows how to estimate both your used storage and the total space allocated to your origin:
Some browsers (all modern ones) also provide a navigator.storage.persist() method to request persistent storage, preventing the browser from automatically clearing your data if the user’s device runs low on space. Note that users might deny such requests, or the request might fail silently on stricter environments. Always handle these outcomes gracefully and design your app to degrade if persistent storage is unavailable.
The best way to handle real-world usage is to test for low storage conditions and large data sets in different environments. You can fill up the space manually by writing repetitive test data or running scripts that bulk-insert documents until an error occurs.
-
Real-time usage monitors or dashboards can keep track of your navigator.storage.estimate() results, letting you see how close you are to the max limit in production. Developer tools in Chrome or Firefox can simulate limited storage situations, which is crucial for QA:
-
0:42
Simulate low storage quota with DevTools
-
-
This short tutorial shows how you can artificially reduce available storage in Google Chrome’s dev tools to see how your app behaves when nearing or exceeding the quota.
When the user’s device is too full or your app exceeds the allotted quota, most browsers will throw a QuotaExceededError (or similarly named exception) when trying to store additional data. Often, the request to IndexedDB simply fails with an error event. Handling this gracefully is essential to avoid crashes or data corruption.
-
A typical approach is to wrap your write operations in try/catch blocks or in onsuccess / onerror event callbacks. If you detect a quota error, you can prompt the user to clear out old items or reduce the scope of offline data. Some apps implement a fallback system that removes less critical documents to free space and then retries the write.
-
try {
- const tx = db.transaction('largeStore', 'readwrite');
- const store = tx.objectStore('largeStore');
- await store.add(hugeData, someKey);
- await tx.done;
-} catch (error) {
- if (error.name === 'QuotaExceededError') {
- console.warn('IndexedDB quota exceeded. Cleanup or prompt user to free space.');
- // Optionally remove older data or show a UI hint:
- // removeOldDocuments();
- // displayStorageFullDialog();
- } else {
- // handle other errors
- console.error('IndexedDB write error:', error);
- }
-}
Even if you plan well, your app might need more storage than a single origin typically allows. There are a few advanced tactics you can use:
-
If you store binary data such as images or videos, consider compressing them via the Compression Streams API. For textual or JSON data, a library like RxDB supports built-in key-compression to shorten field names or entire documents. This can be extremely helpful when storing large sets of objects:
Sharding data across multiple subdomains or iframes is another trick, though it complicates communication. When you need truly massive offline data, you might store part of the data under sub1.yoursite.com and another chunk under sub2.yoursite.com, using postMessage() to coordinate. This can circumvent single-origin limitations, but it introduces extra complexity. Another effective method is to let data expire automatically—perhaps older records are removed if they haven’t been accessed for a certain period.
There is no explicit cap on how large an individual object or record in IndexedDB can be, other than the overall disk quota. If you attempt to store one extremely large object, you will eventually hit browser memory constraints or the global storage quota. In practice, you’ll encounter out-of-memory issues in JavaScript before IndexedDB itself refuses a single large write. A helpful test can be seen in this JSFiddle experiment where you see browsers can crash when creating massive in-memory objects.
-
Is There a Time Limit for Data Stored in IndexedDB?
-
IndexedDB data can remain indefinitely as long as the user does not clear the browser’s data or the origin does not run afoul of automated eviction policies (e.g., Safari or Android might remove large caches for sites unused over a long period when space is needed). Typically, there is no “time limit,” but ephemeral modes or incognito sessions have their own rules. If you rely on permanent offline data, request persistent storage and handle the possibility that the user or the OS could still remove your data under extreme conditions. Especially Safari is known to be very fast in deleting local data.
Learn more by checking the IndexedDB official docs, which detail store design, error handling, and quota usage. If you need a straightforward way to manage large offline data with compression and conflict resolution, explore the RxDB Quickstart. You can also join the community on GitHub to share tips on overcoming the IndexedDB max storage size limit in production environments.
-
-
\ No newline at end of file
diff --git a/docs/articles/indexeddb-max-storage-limit.md b/docs/articles/indexeddb-max-storage-limit.md
deleted file mode 100644
index 640da1fb8f8..00000000000
--- a/docs/articles/indexeddb-max-storage-limit.md
+++ /dev/null
@@ -1,153 +0,0 @@
-# IndexedDB Max Storage Size Limit - Detailed Best Practices
-
-> Learn how browsers enforce IndexedDB storage size limits, how to test and handle quota exceeded errors, and best practices for storing large amounts of data offline.
-
-
-
-import {VideoBox} from '@site/src/components/video-box';
-
-# IndexedDB Max Storage Size Limit
-
-IndexedDB is widely known as the primary browser-based storage API for large client-side data, particularly valuable for modern offline-first applications. These apps aim to keep everything functional and interactive even without an internet connection, which naturally demands substantial local storage. However, IndexedDB has various size limits depending on the browser, disk space, and user settings. Being aware of these constraints is crucial so you can avoid quota errors and deliver a seamless user experience without unexpected data loss.
-
-Offline-first apps have grown in popularity because they provide immediate feedback, zero-latency interactions, and resilience in poor network conditions. Storing big data sets, or even entire data models, in IndexedDB has become far more common than in the era of small localStorage or cookie usage. But all this local data is subject to quotas, and that’s exactly what this guide will help you understand and manage.
-
-## Why IndexedDB Has a Storage Limit
-
-Browsers need a way to curb runaway disk usage and safeguard user resources. This is accomplished through **quota management** policies, which can vary among Chrome, Firefox, Safari, Edge, and others. Some browsers use a percentage of your total disk space, while others rely on a fixed maximum or dynamic approach per origin. These policies are designed to prevent malicious or poorly optimized web pages from consuming an unreasonable amount of user storage.
-
-Chrome (and Chromium-based browsers) typically allow you to use a percentage of the user’s free disk space, whereas Firefox historically prompts users to allow more than 5 MB in mobile or 50 MB in desktop. Safari often sets tighter maximum caps, especially on iOS devices. Edge aligns closely with Chrome’s rules but can also include enterprise or corporate policy overrides. Understanding these default or dynamic limits prepares you to plan your app’s storage needs appropriately.
-
-## Browser-Specific IndexedDB Limits
-
-IndexedDB size quotas differ significantly across browsers and platforms. While there isn’t a universal rule, the following table summarizes approximate limits and any notes or caveats you should be aware of:
-
-| Browser | Approx. Limit | Notes |
-|----------------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
-| Chrome/Chromium | Up to ~80% of free disk, per origin cap | Often cited as 60 GB on a 100 GB drive. Shared pool approach. Quota usage can prompt partial or extended user approvals. |
-| Firefox | ~2 GB (desktop) or ~5 MB initial for mobile | Older versions asked permission at 50 MB for desktop. Ephemeral/incognito sessions may require repeated user prompts. |
-| Safari (iOS) | ~1 GB per origin (variable) | Historically stricter. iOS devices limit quotas further. Behavior can differ between iOS Safari versions or iPadOS. |
-| Edge | Similar to Chrome’s 80% of free space | Can be influenced by Windows enterprise policies. Generally aligned with Chromium approach. |
-| iOS Safari | Typically 1 GB, can be less on older iOS | Early iOS versions were known for more aggressive quotas and data eviction on low space. |
-| Android Chrome | Similar to desktop Chrome | May exhibit warnings in especially low-storage devices. The same 80% free space logic generally applies. |
-
-Historically, these limits have evolved. For instance, older Firefox versions included `dom.indexedDB.warningQuota`, showing a 50 MB prompt on desktop or a 5 MB prompt on mobile—many developers wrote about these notifications on Stack Overflow. Since around 2015, Firefox has changed its quota approach significantly. Likewise, Safari used to limit data more aggressively on older iOS versions. Some older tutorials suggest comparing IndexedDB to localStorage, but modern browsers allow far larger and more flexible storage with IndexedDB than the old localStorage or cookie-based setups.
-
----
-
-## Checking Your Current IndexedDB Usage
-
-To assess where your app stands relative to these storage limits, you can use the **Storage Estimation API**. The snippet below shows how to estimate both your used storage and the total space allocated to your origin:
-
-```js
-const quota = await navigator.storage.estimate();
-const totalSpace = quota.quota;
-const usedSpace = quota.usage;
-console.log('Approx total allocated space:', totalSpace);
-console.log('Approx used space:', usedSpace);
-```
-
-[Some browsers (all modern ones)](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist#browser_compatibility) also provide a `navigator.storage.persist()` method to request persistent storage, preventing the browser from automatically clearing your data if the user’s device runs low on space. Note that users might deny such requests, or the request might fail silently on stricter environments. Always handle these outcomes gracefully and design your app to degrade if persistent storage is unavailable.
-
-## Testing Your App’s IndexedDB Quotas
-
-The best way to handle real-world usage is to test for low storage conditions and large data sets in different environments. You can fill up the space manually by writing repetitive test data or running scripts that bulk-insert documents until an error occurs.
-
-Real-time usage monitors or dashboards can keep track of your `navigator.storage.estimate()` results, letting you see how close you are to the max limit in production. Developer tools in Chrome or Firefox can simulate limited storage situations, which is crucial for QA:
-
-
-
-
-
-This short tutorial shows how you can artificially reduce available storage in Google Chrome’s dev tools to see how your app behaves when nearing or exceeding the quota.
-
-## Handling Errors When Limits Are Reached
-
-When the user’s device is too full or your app exceeds the allotted quota, most browsers will throw a **QuotaExceededError** (or similarly named exception) when trying to store additional data. Often, the request to IndexedDB simply fails with an error event. Handling this gracefully is essential to avoid crashes or data corruption.
-
-A typical approach is to wrap your write operations in try/catch blocks or in `onsuccess` / `onerror` event callbacks. If you detect a quota error, you can prompt the user to clear out old items or reduce the scope of offline data. Some apps implement a fallback system that removes less critical documents to free space and then retries the write.
-
-```js
-try {
- const tx = db.transaction('largeStore', 'readwrite');
- const store = tx.objectStore('largeStore');
- await store.add(hugeData, someKey);
- await tx.done;
-} catch (error) {
- if (error.name === 'QuotaExceededError') {
- console.warn('IndexedDB quota exceeded. Cleanup or prompt user to free space.');
- // Optionally remove older data or show a UI hint:
- // removeOldDocuments();
- // displayStorageFullDialog();
- } else {
- // handle other errors
- console.error('IndexedDB write error:', error);
- }
-}
-```
-
-## Tricks to Exceed the Storage Size Limitation
-
-Even if you plan well, your app might need more storage than a single origin typically allows. There are a few advanced tactics you can use:
-
-If you store binary data such as images or videos, consider compressing them via the Compression Streams API. For textual or [JSON data](./json-based-database.md), a library like [RxDB](/) supports built-in [key-compression](../key-compression.md) to shorten field names or entire documents. This can be extremely helpful when storing large sets of objects:
-
-```ts
-// Example: How key-compression can transform your documents internally
-const uncompressed = {
- "firstName": "Corrine",
- "lastName": "Ziemann",
- "shoppingCartItems": [
- {
- "productNumber": 29857,
- "amount": 1
- },
- {
- "productNumber": 53409,
- "amount": 6
- }
- ]
-};
-const compressed = {
- "|e": "Corrine",
- "|g": "Ziemann",
- "|i": [
- {
- "|h": 29857,
- "|b": 1
- },
- {
- "|h": 53409,
- "|b": 6
- }
- ]
-};
-```
-
-Sharding data across multiple subdomains or iframes is another trick, though it complicates communication. When you need truly massive offline data, you might store part of the data under `sub1.yoursite.com` and another chunk under `sub2.yoursite.com`, using `postMessage()` to coordinate. This can circumvent single-origin limitations, but it introduces extra complexity. Another effective method is to let data expire automatically—perhaps older records are removed if they haven’t been accessed for a certain period.
-
-
-
-
-
-
-
-## IndexedDB Max Size of a Single Object
-
-There is no explicit cap on how large an individual object or record in IndexedDB can be, other than the overall disk quota. If you attempt to store one extremely large object, you will eventually hit browser memory constraints or the global storage quota. In practice, you’ll encounter out-of-memory issues in JavaScript before IndexedDB itself refuses a single large write. A helpful test can be seen in [this JSFiddle experiment](https://jsfiddle.net/sdrqf8om/2/) where you see browsers can crash when creating massive in-memory objects.
-
-## Is There a Time Limit for Data Stored in IndexedDB?
-
-IndexedDB data can remain indefinitely as long as the user does not clear the browser’s data or the origin does not run afoul of automated eviction policies (e.g., Safari or Android might remove large caches for sites unused over a long period when space is needed). Typically, there is no “time limit,” but ephemeral modes or incognito sessions have their own rules. If you rely on permanent offline data, request persistent storage and handle the possibility that the user or the OS could still remove your data under extreme conditions. Especially Safari is known to be very fast in deleting local data.
-
-
-
-## Follow Up
-
-Learn more by checking the [IndexedDB official docs](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API), which detail store design, error handling, and quota usage. If you need a straightforward way to manage large offline data with compression and conflict resolution, explore the [RxDB Quickstart](../quickstart.md). You can also join the community on [GitHub](/code/) to share tips on overcoming the **IndexedDB max storage size limit** in production environments.
diff --git a/docs/articles/ionic-database.html b/docs/articles/ionic-database.html
deleted file mode 100644
index d0340e17971..00000000000
--- a/docs/articles/ionic-database.html
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
-
-RxDB - The Perfect Ionic Database | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In the fast-paced world of mobile app development, hybrid applications have emerged as a versatile solution, offering the best of both worlds - the web and native app experiences. One key challenge these apps face is efficiently storing and querying data on the client's device. Enter RxDB, a powerful client-side database tailored for ionic hybrid applications. In this article, we'll explore how RxDB addresses the requirements of storing and querying data in ionic apps, and why it stands out as a preferred choice.
Ionic (aka Ionic 2 ) hybrid apps combine the strengths of web technologies (HTML, CSS, JavaScript) with native app development to deliver cross-platform applications. They are built using web technologies and then wrapped in a native container to be deployed on various platforms like iOS, Android, and the web. These apps provide a consistent user experience across devices while benefiting from the efficiency and familiarity of web development.
Storing and querying data is a fundamental aspect of any application, including hybrid apps. These apps often need to operate offline, store user-generated content, and provide responsive user interfaces. Therefore, having a reliable and efficient way to manage data on the client's device is crucial.
-
Introducing RxDB as a Client-Side Database for Ionic Apps
-
RxDB steps in as a powerful solution to address the data management needs of ionic hybrid apps. It's a NoSQL client-side database that offers exceptional performance and features tailored to the unique requirements of client-side applications. Let's delve into the key features of RxDB that make it a great fit for these apps.
At its core, RxDB is a NoSQL database that operates with a local-first approach. This means that your app's data is stored and processed primarily on the client's device, reducing the dependency on constant network connectivity. By doing so, RxDB ensures your app remains responsive and functional, even when offline.
The local-first approach adopted by RxDB is a game-changer for hybrid applications. Storing data locally allows your app to function seamlessly without an internet connection, providing users with uninterrupted access to their data. When connectivity is restored, RxDB handles the synchronization of data, ensuring that any changes made offline are appropriately propagated.
One of RxDB's standout features is its implementation of observable queries. This concept allows your app's user interface to be dynamically updated in real time as data changes within the database. RxDB's observables create a bridge between your database and user interface, keeping them in sync effortlessly.
RxDB's NoSQL query engine empowers you to perform powerful queries on your app's data, without the constraints imposed by traditional relational databases. This flexibility is particularly valuable when dealing with unstructured or semi-structured data. With the NoSQL query engine, you can retrieve, filter, and manipulate data according to your app's unique requirements.
RxDB introduces a concept called EventReduce, which optimizes the observation process. Instead of overwhelming your app's UI with every data change, EventReduce filters and batches these changes to provide a smooth and efficient experience. This leads to enhanced app performance, lower resource usage, and ultimately, happier users.
-
Why NoSQL is a Better Fit for Client-Side Applications Compared to relational databases like SQLite
-
When it comes to choosing the right database solution for your client-side applications, NoSQL RxDB presents compelling advantages over traditional options like SQLite. Let's delve into the key reasons why NoSQL RxDB is a superior fit for your ionic hybrid app development.
NoSQL databases, like RxDB, inherently embrace a document-based approach to data storage. This design choice simplifies data replication between clients and servers. With documents representing discrete units of data, you can easily synchronize individual pieces of information without the complexity that can arise when dealing with rows and tables in a relational database like SQLite. This document-centric replication model streamlines the synchronization process and ensures that your app's data remains consistent across devices.
One of the defining features of client-side applications is the ability to function even when offline. NoSQL RxDB excels in this area by supporting a local-first approach. Data is cached on the client's device, enabling the app to remain fully functional even without an internet connection. As connectivity is restored, RxDB handles data synchronization with the server seamlessly. This offline capability ensures a smooth user experience, critical for ionic hybrid apps catering to users in various network conditions.
TypeScript, a popular superset of JavaScript, is renowned for its static typing and enhanced developer experience. NoSQL databases like RxDB are inherently flexible, making them well-suited for TypeScript integration. With well-defined data structures and clear typings, NoSQL RxDB offers improved type safety and easier development when compared to traditional SQL databases like SQLite. This results in reduced debugging time and increased code reliability.
Schema changes are a common occurrence in application development, and dealing with them can be challenging. NoSQL databases, including RxDB, are more forgiving in this aspect. Since documents in NoSQL databases don't enforce a rigid structure like tables in relational databases, schema changes are often simpler to manage. This flexibility makes it easier to evolve your app's data structure over time without the need for complex migration scripts, a notable advantage when compared to SQLite.
RxDB's excellent performance stems from its advanced indexing capabilities, which streamline data retrieval and ensure swift query execution. Additionally, the JSON key compression employed by RxDB minimizes storage overhead, enabling efficient data transfer and quicker loading times. The incorporation of real-time updates through change streams and the EventReduce mechanism further enhances RxDB's performance, delivering a responsive user experience even as data changes are propagated seamlessly.
RxDB's integration into your ionic hybrid app opens up a world of possibilities for efficient data management. Let's explore how to set up RxDB, use it with popular JavaScript frameworks, and take advantage of its diverse storage options.
Getting started with RxDB is a straightforward process. By including the RxDB library in your project, you can quickly start harnessing its capabilities. Begin by installing the RxDB package from the npm registry. Then, configure your database instance to suit your app's needs. This setup process paves the way for seamless data management in your ionic hybrid app.
-For a full instruction, follow the RxDB Quickstart.
-
Using RxDB in Frameworks (React, Angular, Vue.js)
-
RxDB seamlessly integrates with various JavaScript frameworks, ensuring compatibility with your preferred development environment. Whether you're building your ionic hybrid app with React, Angular, or Vue.js, RxDB offers bindings and tools that enable you to leverage its features effortlessly. This compatibility allows you to stay within the comfort zone of your chosen framework while benefiting from RxDB's powerful data management capabilities.
RxDB doesn't limit you to a single storage solution. Instead, it provides a range of RxStorage layers to accommodate diverse use cases. These storage layers offer flexibility and customization, enabling you to tailor your data management strategy to match your app's requirements. Let's explore some of the available RxStorage options:
IndexedDB RxStorage: Leveraging the native browser storage, IndexedDB RxStorage offers reliable data persistence. This storage option is suitable for a wide range of scenarios and is supported by most modern browsers.
-
OPFS RxStorage: Operating within the browser's file system, OPFS RxStorage is a unique choice that can handle larger data volumes efficiently. It's particularly useful for applications that require substantial data storage.
-
Memory RxStorage: Memory RxStorage is perfect for temporary or cache-like data storage. It keeps data in memory, which can result in rapid data access but doesn't provide long-term persistence.
-
SQLite RxStorage: SQLite is the goto database for mobile applications. It is build in on android and iOS devices. The SQLite RxDB storage layer is build upon SQLite and offers the best performance on hybrid apps, like ionic.
-
-
Replication of Data with RxDB between Clients and Servers
-
Efficient data replication between clients and servers is the backbone of modern application development, ensuring that data remains consistent and up-to-date across various devices and platforms. RxDB provides a suite of replication methods that facilitate seamless communication between clients and servers, ensuring that your data is always in sync.
At the heart of RxDB's replication capabilities lies a sophisticated algorithm designed to manage data synchronization between clients and servers. This algorithm intelligently handles data changes, conflict resolution, and network connectivity fluctuations, resulting in reliable and efficient data replication. With the RxDB replication algorithm, your application can maintain data consistency across devices without unnecessary complexities.
-
-
-
CouchDB Replication:
-RxDB's integration with CouchDB replication presents a powerful way to synchronize data between clients and servers. CouchDB, a well-established NoSQL database, excels at distributed and decentralized data scenarios. By utilizing RxDB's CouchDB replication, you can establish bidirectional synchronization between your RxDB-powered client and a CouchDB server. This synchronization ensures that data updates made on either end are seamlessly propagated to the other, facilitating collaboration and data sharing.
-
-
-
Firestore Replication:
-Firestore, Google's cloud-hosted NoSQL database, offers another avenue for data replication in RxDB. With Firestore replication, you can establish a connection between your RxDB-powered app and Firestore's cloud infrastructure. This integration provides real-time updates to data across multiple instances of your application, ensuring that users always have access to the latest information. RxDB's support for Firestore replication empowers you to build dynamic and responsive applications that thrive in today's fast-paced digital landscape.
-
-
-
WebRTC Replication:
-Peer-to-peer (P2P) replication via WebRTC introduces a cutting-edge approach to data synchronization in RxDB. P2P replication allows devices to communicate directly with each other, bypassing the need for a central server. This method proves invaluable in scenarios where network connectivity is limited or unreliable. With WebRTC replication, devices can exchange data directly, enabling collaboration and information sharing even in challenging network conditions.
When it comes to securing sensitive data in your Ionic applications, RxDB emerges as a powerful alternative to traditional secure storage solutions. Let's delve into why RxDB is an exceptional choice for safeguarding your data while providing additional benefits.
RxDB offers an on-device encryption plugin, adding an extra layer of security to your app's data. This means that data stored within the RxDB database can be encrypted, ensuring that even if the device falls into the wrong hands, the sensitive information remains inaccessible without the proper decryption key. This level of data protection is crucial for applications that deal with personal or confidential information. Encryption runs either with AES on crypto-js or with the Web Crypto API which is faster and more secure.
Security should never compromise functionality. RxDB excels in this area by allowing your application to operate seamlessly even when offline. The locally stored encrypted data remains accessible and functional, enabling users to interact with the app's features even without an active internet connection. This offline capability ensures that user data is secure, while the app continues to deliver a responsive and uninterrupted experience.
Ensuring data consistency between your client-side application and backend is a key concern for developers. RxDB simplifies this process with its straightforward replication setup. You can effortlessly configure data synchronization between your local RxDB instance and your backend server. This replication capability ensures that encrypted data remains up-to-date and aligned with the central database, enhancing data integrity and security.
In addition to security and offline capabilities, RxDB also offers data compression. This means that the data stored on the client's device is efficiently compressed, reducing storage requirements and improving overall app performance. This compression ensures that your app remains responsive and efficient, even as data volumes grow.
In addition to its security features, RxDB offers cost-effective benefits. RxDB is priced more affordably compared to some other secure storage solutions, making it an attractive option for developers seeking robust security without breaking the bank. For many users, the free version of RxDB provides ample features to meet their application's security and data management needs.
-
-
\ No newline at end of file
diff --git a/docs/articles/ionic-database.md b/docs/articles/ionic-database.md
deleted file mode 100644
index 42c856913c3..00000000000
--- a/docs/articles/ionic-database.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# RxDB - The Perfect Ionic Database
-
-> Supercharge your Ionic hybrid apps with RxDB's offline-first database. Experience real-time sync, top performance, and easy replication.
-
-# Ionic Storage - RxDB as database for hybrid apps
-
-In the fast-paced world of mobile app development, **hybrid applications** have emerged as a versatile solution, offering the best of both worlds - the web and native app experiences. One key challenge these apps face is efficiently storing and querying data on the **client's device**. Enter [RxDB](https://rxdb.info/), a powerful client-side database tailored for ionic hybrid applications. In this article, we'll explore how RxDB addresses the requirements of storing and querying data in ionic apps, and why it stands out as a preferred choice.
-
-
-
-
-
-
-
-## What are Ionic Hybrid Apps?
-
-Ionic (aka Ionic 2 ) hybrid apps combine the strengths of web technologies (HTML, CSS, JavaScript) with native app development to deliver cross-platform applications. They are built using web technologies and then wrapped in a native container to be deployed on various platforms like iOS, Android, and the web. These apps provide a consistent user experience across devices while benefiting from the efficiency and familiarity of web development.
-
-## Storing and Querying Data in an Ionic App
-
-Storing and querying data is a fundamental aspect of any application, including hybrid apps. These apps often need to operate offline, store user-generated content, and provide responsive user interfaces. Therefore, having a reliable and efficient way to manage data on the client's device is crucial.
-
-## Introducing RxDB as a Client-Side Database for Ionic Apps
-RxDB steps in as a powerful solution to address the data management needs of ionic hybrid apps. It's a NoSQL client-side database that offers exceptional performance and features tailored to the unique requirements of client-side applications. Let's delve into the key features of RxDB that make it a great fit for these apps.
-
-### Getting Started with RxDB
-
-
-
-
-
-
-
-### What is RxDB?
-
-At its core, [RxDB](https://rxdb.info/) is a **NoSQL** database that operates with a [local-first](../offline-first.md) approach. This means that your app's data is stored and processed primarily on the client's device, reducing the dependency on constant network connectivity. By doing so, RxDB ensures your app remains responsive and functional, even when offline.
-
-### Local-First Approach
-The [local-first](../offline-first.md) approach adopted by RxDB is a game-changer for hybrid applications. Storing data locally allows your app to function seamlessly without an internet connection, providing users with uninterrupted access to their data. When connectivity is restored, RxDB handles the synchronization of data, ensuring that any changes made offline are appropriately propagated.
-
-### Observable Queries
-One of RxDB's standout features is its implementation of observable queries. This concept allows your app's user interface to be dynamically updated in real time as data changes within the database. RxDB's observables create a bridge between your database and user interface, keeping them in sync effortlessly.
-
-
-
-### NoSQL Query Engine
-RxDB's NoSQL query engine empowers you to perform powerful queries on your app's data, without the constraints imposed by traditional relational databases. This flexibility is particularly valuable when dealing with unstructured or semi-structured data. With the NoSQL query engine, you can retrieve, filter, and manipulate data according to your app's unique requirements.
-
-```ts
-const foundDocuments = await myDatabase.todos.find({
- selector: {
- done: {
- $eq: false
- }
- }
-}).exec();
-```
-
-### Great Observe Performance with EventReduce
-RxDB introduces a concept called [EventReduce](https://github.com/pubkey/event-reduce), which optimizes the observation process. Instead of overwhelming your app's UI with every data change, EventReduce filters and batches these changes to provide a smooth and efficient experience. This leads to enhanced app performance, lower resource usage, and ultimately, happier users.
-
-## Why NoSQL is a Better Fit for Client-Side Applications Compared to relational databases like SQLite
-When it comes to choosing the right database solution for your client-side applications, NoSQL RxDB presents compelling advantages over traditional options like SQLite. Let's delve into the key reasons why NoSQL RxDB is a superior fit for your ionic hybrid app development.
-
-### Easier Document-Based Replication
-NoSQL databases, like RxDB, inherently embrace a document-based approach to [data storage](./ionic-storage.md). This design choice simplifies data [replication](../replication.md) between clients and servers. With documents representing discrete units of data, you can easily synchronize individual pieces of information without the complexity that can arise when dealing with rows and tables in a relational database like SQLite. This document-centric replication model streamlines the synchronization process and ensures that your app's data remains consistent across devices.
-
-### Offline Capable
-One of the defining features of client-side applications is the ability to function even when offline. NoSQL RxDB excels in this area by supporting a local-first approach. Data is cached on the client's device, enabling the app to remain fully functional even without an internet connection. As connectivity is restored, RxDB handles data synchronization with the server seamlessly. This offline capability ensures a smooth user experience, critical for ionic hybrid apps catering to users in various network conditions.
-
-### NoSQL Has Better TypeScript Support
-TypeScript, a popular superset of JavaScript, is renowned for its static typing and enhanced developer experience. NoSQL databases like RxDB are inherently flexible, making them well-suited for TypeScript integration. With well-defined data structures and clear typings, NoSQL RxDB offers [improved type safety](../tutorials/typescript.md) and easier development when compared to traditional SQL databases like SQLite. This results in reduced debugging time and increased code reliability.
-
-### Easier [Schema Migration](../migration-schema.md) with NoSQL Documents
-Schema changes are a common occurrence in application development, and dealing with them can be challenging. NoSQL databases, including RxDB, are more forgiving in this aspect. Since documents in NoSQL databases don't enforce a rigid structure like tables in relational databases, schema changes are often simpler to manage. This flexibility makes it easier to evolve your app's data structure over time without the need for complex migration scripts, a notable advantage when compared to SQLite.
-
-## Great Performance
-RxDB's [excellent performance](../rx-storage-performance.md) stems from its advanced indexing capabilities, which streamline data retrieval and ensure swift query execution. Additionally, the [JSON key compression](../key-compression.md) employed by RxDB minimizes storage overhead, enabling efficient data transfer and quicker loading times. The incorporation of real-time updates through change streams and the **EventReduce mechanism** further enhances RxDB's performance, delivering a responsive user experience even as data changes are propagated seamlessly.
-
-## Using RxDB in an Ionic Hybrid App
-RxDB's integration into your ionic hybrid app opens up a world of possibilities for efficient data management. Let's explore how to set up RxDB, use it with popular JavaScript frameworks, and take advantage of its diverse storage options.
-
-### Setup RxDB
-Getting started with RxDB is a straightforward process. By including the RxDB library in your project, you can quickly start harnessing its capabilities. Begin by installing the [RxDB package](https://www.npmjs.com/package/rxdb) from the npm registry. Then, configure your database instance to suit your app's needs. This setup process paves the way for seamless data management in your ionic hybrid app.
-For a full instruction, follow the [RxDB Quickstart](https://rxdb.info/quickstart.html).
-
-### Using RxDB in Frameworks (React, Angular, Vue.js)
-RxDB seamlessly integrates with various JavaScript frameworks, ensuring compatibility with your preferred development environment. Whether you're building your ionic hybrid app with [React](./react-database.md), [Angular](./angular-database.md), or [Vue.js](./vue-database.md), RxDB offers bindings and tools that enable you to leverage its features effortlessly. This compatibility allows you to stay within the comfort zone of your chosen framework while benefiting from RxDB's powerful data management capabilities.
-
-### Different RxStorage Layers for RxDB
-RxDB doesn't limit you to a single storage solution. Instead, it provides a range of RxStorage layers to accommodate diverse use cases. These storage layers offer flexibility and customization, enabling you to tailor your data management strategy to match your app's requirements. Let's explore some of the available RxStorage options:
-
-- [LocalStorage RxStorage](../rx-storage-localstorage.md): Based on the browsers [localStorage](./localstorage.md). Easy to set up and fast for small datasets.
-- [IndexedDB RxStorage](../rx-storage-indexeddb.md): Leveraging the native browser storage, IndexedDB RxStorage offers reliable data persistence. This storage option is suitable for a wide range of scenarios and is supported by most modern browsers.
-- [OPFS RxStorage](../rx-storage-opfs.md): Operating within the browser's file system, OPFS RxStorage is a unique choice that can handle larger data volumes efficiently. It's particularly useful for applications that require substantial data storage.
-- [Memory RxStorage](../rx-storage-memory.md): Memory RxStorage is perfect for temporary or cache-like data storage. It keeps data in memory, which can result in rapid data access but doesn't provide long-term persistence.
-- [SQLite RxStorage](../rx-storage-sqlite.md): SQLite is the goto database for mobile applications. It is build in on android and iOS devices. The SQLite RxDB storage layer is build upon SQLite and offers the best performance on hybrid apps, like ionic.
-
-## Replication of Data with RxDB between Clients and Servers
-Efficient data replication between clients and servers is the backbone of modern application development, ensuring that data remains consistent and up-to-date across various devices and platforms. RxDB provides a suite of replication methods that facilitate seamless communication between clients and servers, ensuring that your data is always in sync.
-
-### RxDB Replication Algorithm
-At the heart of RxDB's replication capabilities lies a sophisticated [algorithm](../replication.md) designed to manage data synchronization between clients and servers. This algorithm intelligently handles data changes, conflict resolution, and network connectivity fluctuations, resulting in reliable and efficient data replication. With the RxDB replication algorithm, your application can maintain data consistency across devices without unnecessary complexities.
-
-- [CouchDB Replication](../replication-couchdb.md):
-RxDB's integration with CouchDB replication presents a powerful way to synchronize data between clients and servers. CouchDB, a well-established NoSQL database, excels at distributed and decentralized data scenarios. By utilizing RxDB's CouchDB replication, you can establish bidirectional synchronization between your RxDB-powered client and a CouchDB server. This synchronization ensures that data updates made on either end are seamlessly propagated to the other, facilitating collaboration and data sharing.
-
-- [Firestore Replication](../replication-firestore.md):
-Firestore, Google's cloud-hosted NoSQL database, offers another avenue for data replication in RxDB. With Firestore replication, you can establish a connection between your RxDB-powered app and Firestore's cloud infrastructure. This integration provides real-time updates to data across multiple instances of your application, ensuring that users always have access to the latest information. RxDB's support for Firestore replication empowers you to build dynamic and responsive applications that thrive in today's fast-paced digital landscape.
-
-- [WebRTC Replication](../replication-webrtc.md):
-Peer-to-peer (P2P) replication via WebRTC introduces a cutting-edge approach to data synchronization in RxDB. P2P replication allows devices to communicate directly with each other, bypassing the need for a central server. This method proves invaluable in scenarios where network connectivity is limited or unreliable. With WebRTC replication, devices can exchange data directly, enabling collaboration and information sharing even in challenging network conditions.
-
-## RxDB as an Alternative for Ionic Secure Storage
-When it comes to securing sensitive data in your Ionic applications, RxDB emerges as a powerful alternative to traditional secure storage solutions. Let's delve into why RxDB is an exceptional choice for safeguarding your data while providing additional benefits.
-
-### RxDB On-Device Encryption Plugin
-RxDB offers an [on-device encryption plugin](https://rxdb.info/encryption.html), adding an extra layer of security to your app's data. This means that data stored within the RxDB database can be encrypted, ensuring that even if the device falls into the wrong hands, the sensitive information remains inaccessible without the proper decryption key. This level of data protection is crucial for applications that deal with personal or confidential information. Encryption runs either with `AES` on `crypto-js` or with the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) which is faster and more secure.
-
-### Works Offline
-Security should never compromise functionality. RxDB excels in this area by allowing your application to operate seamlessly even when offline. The locally stored encrypted data remains accessible and functional, enabling users to interact with the app's features even without an active internet connection. This offline capability ensures that user data is secure, while the app continues to deliver a responsive and uninterrupted experience.
-
-### Easy-to-Setup Replication with Your Backend
-Ensuring data consistency between your client-side application and backend is a key concern for developers. RxDB simplifies this process with its straightforward replication setup. You can effortlessly configure data synchronization between your local RxDB instance and your backend server. This replication capability ensures that encrypted data remains up-to-date and aligned with the central database, enhancing data integrity and security.
-
-### Compression of Client-Side Stored Data
-In addition to security and offline capabilities, RxDB also offers [data compression](https://rxdb.info/key-compression.html). This means that the data stored on the client's device is efficiently compressed, reducing storage requirements and improving overall app performance. This compression ensures that your app remains responsive and efficient, even as data volumes grow.
-
-### Cost-Effective Solution
-In addition to its security features, RxDB offers cost-effective benefits. RxDB is [priced more affordably](/premium/) compared to some other secure storage solutions, making it an attractive option for developers seeking robust security without breaking the bank. For many users, the free version of RxDB provides ample features to meet their application's security and data management needs.
-
-## Follow Up
-
-- Try out the [RxDB ionic example project](https://github.com/pubkey/rxdb/tree/master/examples/ionic2)
-- Try out the [RxDB Quickstart](https://rxdb.info/quickstart.html)
-- Join the [RxDB Chat](https://rxdb.info/chat/)
diff --git a/docs/articles/ionic-storage.html b/docs/articles/ionic-storage.html
deleted file mode 100644
index 4b38cd67121..00000000000
--- a/docs/articles/ionic-storage.html
+++ /dev/null
@@ -1,197 +0,0 @@
-
-
-
-
-
-RxDB - Local Ionic Storage with Encryption, Compression & Sync | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
RxDB - Local Ionic Storage with Encryption, Compression & Sync
-
When building Ionic apps, developers face the challenge of choosing a robust Ionic storage mechanism that supports:
-
-
Offline-First usage
-
Data Encryption to protect sensitive content
-
Compression to reduce storage usage and improve performance
-
Seamless Sync with any backend for real-time updates
-
-
RxDB (Reactive Database) offers all these features in a single, local-first database solution tailored to Ionic and other hybrid frameworks. Keep reading to learn how RxDB solves the most common storage pitfalls in hybrid app development while providing unmatched flexibility.
Offline functionality is crucial for modern mobile applications, particularly when devices encounter unreliable or slow networks. RxDB stores all data locally so your Ionic app can run seamlessly without needing a continuous internet connection. When a network is available again, RxDB automatically synchronizes changes with your backend - no extra code required.
Large or repetitive data can significantly slow down devices with minimal memory. RxDB's key-compression feature decreases document size stored on the device, improving overall performance by:
In addition to functioning fully offline, RxDB supports advanced replication options. Your Ionic app can instantly sync updates with any backend (CouchDB, Firestore, GraphQL, or custom REST), maintaining a real-time user experience. Plus, RxDB handles conflicts gracefully - meaning less worry about clashing user edits.
RxDB runs with a NoSQL approach and integrates seamlessly into Ionic Angular or other frameworks you might use with Ionic. You can extend or replace storage backends, add encryption, or build advanced offline-first features with minimal overhead.
-
-
Quick Start: Implementing RxDB with LocalSTorage Storage
-
For a simple proof-of-concept or testing environment in Ionic, you can use localstorage as your underlying storage. Later, if you need better native performance, you can switch to the SQLite storage offered by the RxDB Premium plugins.
-
-
Install RxDB
-
-
npm install rxdb rxjs
-
-
Initialize the Database
-
-
import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-async function initDB() {
- const db = await createRxDatabase({
- name: 'myionicdb',
- storage: getRxStorageLocalstorage(),
- multiInstance: false // or true if you plan multi-tab usage
- // Note: If you need encryption, set `password` here
- });
-
- await db.addCollections({
- notes: {
- schema: {
- title: 'notes schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- content: { type: 'string' },
- timestamp: { type: 'number' }
- },
- required: ['id']
- }
- }
- });
-
- return db;
-}
-
-
Ready to Upgrade Later?
-
-
When you need the best performance on mobile devices, purchase the RxDB PremiumSQLite Storage and replace getRxStorageLocalstorage() with getRxStorageSQLite() - your app logic remains largely the same. You only have to change the configuration.
To secure local data, add the crypto-js encryption plugin (free version) or the premium web-crypto plugin. Below is an example using the free crypto-js plugin:
With keyCompression: true, RxDB shortens field names internally, significantly reducing document size. This helps both stored data and network transport during replication.
For Ionic storage that supports offline-first operations, built-in encryption, optional data compression, and live syncing with any backend, RxDB provides a powerful solution. Start quickly with localstorage for local development and testing - then scale up to the premium SQLite storage for optimal performance on production mobile devices.
-
-
\ No newline at end of file
diff --git a/docs/articles/ionic-storage.md b/docs/articles/ionic-storage.md
deleted file mode 100644
index 647b32d6e4d..00000000000
--- a/docs/articles/ionic-storage.md
+++ /dev/null
@@ -1,195 +0,0 @@
-# RxDB - Local Ionic Storage with Encryption, Compression & Sync
-
-> The best Ionic storage solution? RxDB empowers your hybrid apps with offline-first capabilities, secure encryption, data compression, and seamless data syncing to any backend.
-
-# RxDB - Local Ionic Storage with Encryption, Compression & Sync
-
-When building **Ionic** apps, developers face the challenge of choosing a robust **Ionic storage** mechanism that supports:
-- **Offline-First** usage
-- **Data Encryption** to protect sensitive content
-- **Compression** to reduce storage usage and improve performance
-- **Seamless Sync** with any backend for real-time updates
-
-[RxDB](https://rxdb.info/) (Reactive Database) offers all these features in a single, [local-first](./local-first-future.md) database solution tailored to **Ionic** and other hybrid frameworks. Keep reading to learn how RxDB solves the most common storage pitfalls in hybrid app development while providing unmatched flexibility.
-
-
-
-
-
-
-
-## Why RxDB for Ionic Storage?
-
-### 1. Offline-Ready NoSQL Storage
-[Offline functionality](../offline-first.md) is crucial for modern mobile applications, particularly when devices encounter unreliable or slow networks. RxDB stores all data **locally** so your Ionic app can run seamlessly without needing a continuous internet connection. When a network is available again, RxDB automatically synchronizes changes with your backend - no extra code required.
-
-### 2. Powerful Encryption
-Securing on-device data is paramount when handling sensitive information. RxDB includes [encryption plugins](../encryption.html) that let you:
-- **Encrypt** data fields at rest with AES
-- Invalidate data access by simply withholding the password
-- Keep your users' data confidential, even if the device is stolen
-
-This built-in encryption sets RxDB apart from many other Ionic storage options that lack integrated security.
-
-### 3. Built-In Data Compression
-Large or repetitive data can significantly slow down devices with minimal memory. RxDB's [key-compression](../key-compression.md) feature decreases document size stored on the device, improving overall performance by:
-- Reducing disk usage
-- Accelerating queries
-- Minimizing network overhead when syncing
-
-### 4. Real-Time Sync & Conflict Handling
-In addition to functioning fully offline, RxDB supports advanced [replication](../replication.md) options. Your Ionic app can instantly sync updates with any backend ([CouchDB](../replication-couchdb.md), [Firestore](../replication-firestore.md), [GraphQL](../replication-graphql.md), or [custom REST](../replication-http.md)), maintaining a [real-time](./realtime-database.md) user experience. Plus, RxDB handles [conflicts](../transactions-conflicts-revisions.md) gracefully - meaning less worry about clashing user edits.
-
-
-
-### 5. Easy to Adopt and Extend
-RxDB runs with a **NoSQL** approach and integrates seamlessly into [Ionic Angular](https://ionicframework.com/docs/angular/overview) or other frameworks you might use with Ionic. You can extend or replace storage backends, add encryption, or build advanced offline-first features with minimal overhead.
-
-
-
-
-
-
-
-## Quick Start: Implementing RxDB with LocalSTorage Storage
-
-For a simple proof-of-concept or testing environment in [Ionic](./ionic-database.md), you can use [localstorage](../rx-storage-localstorage.md) as your underlying storage. Later, if you need better native performance, you can **switch to the SQLite storage** offered by the [RxDB Premium plugins](https://rxdb.info/premium/).
-
-1. **Install RxDB**
-```bash
-npm install rxdb rxjs
-```
-
-2. **Initialize the Database**
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-async function initDB() {
- const db = await createRxDatabase({
- name: 'myionicdb',
- storage: getRxStorageLocalstorage(),
- multiInstance: false // or true if you plan multi-tab usage
- // Note: If you need encryption, set `password` here
- });
-
- await db.addCollections({
- notes: {
- schema: {
- title: 'notes schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- content: { type: 'string' },
- timestamp: { type: 'number' }
- },
- required: ['id']
- }
- }
- });
-
- return db;
-}
-```
-
-3. **Ready to Upgrade Later?**
-
-When you need the best performance on mobile devices, purchase the RxDB [Premium](/premium/) [SQLite Storage](../rx-storage-sqlite.md) and replace `getRxStorageLocalstorage()` with `getRxStorageSQLite()` - your app logic remains largely the same. You only have to change the configuration.
-
-## Encryption Example
-
-To secure local data, add the crypto-js [encryption plugin](../encryption.md) (free version) or the [premium](/premium/) web-crypto plugin. Below is an example using the free crypto-js plugin:
-
-```ts
-import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { createRxDatabase } from 'rxdb/plugins/core';
-
-async function initEncryptedDB() {
- const encryptedStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageLocalstorage()
- });
-
- const db = await createRxDatabase({
- name: 'secureIonicDB',
- storage: encryptedStorage,
- password: 'myS3cretP4ssw0rd'
- });
-
- await db.addCollections({
- secrets: {
- schema: {
- title: 'secret schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- text: { type: 'string' }
- },
- required: ['id'],
- // all fields in this array will be stored encrypted:
- encrypted: ['text']
- }
- }
- });
-
- return db;
-}
-```
-
-With encryption enabled:
-
-- `text` is automatically encrypted at rest.
-- [Queries](../rx-query.md) on encrypted fields are not directly possible (since data is encrypted), but once a document is loaded, RxDB decrypts it for normal usage.
-
-## Compression Example
-
-To minimize the storage footprint, RxDB offers a [key-compression](../key-compression.md) feature. You can enable it in your schema:
-
-```ts
-await db.addCollections({
- logs: {
- schema: {
- title: 'logs schema',
- version: 0,
- keyCompression: true, // enable compression
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- message: { type: 'string' },
- createdAt: { type: 'string', format: 'date-time' }
- }
- }
- }
-});
-```
-
-With `keyCompression: true`, RxDB shortens field names internally, significantly reducing document size. This helps both stored data and network transport during replication.
-
-## RxDB vs. Other Ionic Storage Options
-
-**Ionic Native Storage** or **Capacitor-based** key-value stores may handle small amounts of data but lack advanced features like:
-
-- Complex queries
-- Full NoSQL document model
-- [Offline-first](../offline-first.md) [sync](../replication.md)
-- Encryption & key compression out of the box
-- RxDB stands out by delivering all these capabilities in a unified library.
-
-## Follow Up
-
-For Ionic storage that supports offline-first operations, built-in encryption, optional data compression, and live syncing with any backend, RxDB provides a powerful solution. Start quickly with [localstorage](../rx-storage-localstorage.md) for local development and testing - then scale up to the premium SQLite storage for optimal performance on production mobile devices.
-
-Ready to learn more?
-
-- Explore the [RxDB Quickstart Guide](../quickstart.md)
-- Check out [RxDB Encryption](../encryption.md) to protect user data
-- Learn about [SQLite Storage](../rx-storage-sqlite.md) in [RxDB Premium](/premium/) for top [performance](../rx-storage-performance.md) on mobile.
-- Join our community on the [RxDB Chat](/chat/)
-
-**RxDB** - The ultimate toolkit for Ionic developers seeking offline-first, secure, and compressed local data, with real-time sync to any server.
diff --git a/docs/articles/javascript-vector-database.html b/docs/articles/javascript-vector-database.html
deleted file mode 100644
index e816e5ea115..00000000000
--- a/docs/articles/javascript-vector-database.html
+++ /dev/null
@@ -1,490 +0,0 @@
-
-
-
-
-
-Local JavaScript Vector Database that works offline | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Local Vector Database with RxDB and transformers.js in JavaScript
-
The local-first revolution is here, changing the way we build apps! Imagine a world where your app's data lives right on the user's device, always available, even when there's no internet. That's the magic of local-first apps. Not only do they bring faster performance and limitless scalability, but they also empower users to work offline without missing a beat. And leading the charge in this space are local database solutions, like RxDB.
-
-
But here's where things get even more exciting: when building local-first apps, traditional databases often fall short. They're great at searching for exact matches, like numbers or strings, but what if you want to search by meaning, like sifting through emails to find a specific topic? Sure, you could use RegExp, but to truly unlock the power of semantic search and similarity-based queries, you need something more cutting-edge. Something that really understands the content of the data.
-
Enter vector databases, the game-changers for searching data by meaning! They have unlocked these new possibilities for storing and querying data, especially in tasks requiring semantic search and similarity-based queries. With the help of a machine learning model, data is transformed into a vector representation that can be stored, queried and compared in a database.
-
But unfortunately, most vector databases are designed for server-side use, typically running in large cloud clusters, not to run on a users device. To fix that, in this article, we will combine RxDB and transformers.js to create a local vector database running in the browser with JavaScript. It stores data in IndexedDB, and uses a machine learning model with WebAssembly locally, without the need for external servers.
-
-
-
transformers.js is a powerful framework that allows machine learning models to run directly within JavaScript using WebAssembly or WebGPU.
-
-
-
RxDB is a NoSQL, local-first database with a flexible storage layer that can run on any JavaScript runtime, including browsers and mobile environments. (You are reading this article on the RxDB docs).
-
-
-
A local vector database offers several key benefits:
-
-
Zero network latency: Data is processed locally on the user's device, ensuring near-instant responses.
-
Offline functionality: Data can be queried even without an internet connection.
-
Enhanced privacy: Sensitive information remains on the device, never needing to leave for external processing.
-
Simple setup: No backend servers are required, making deployment straightforward.
-
Cost savings: By running everything locally, you avoid fees for API access or cloud services for large language models.
-
-
note
In this article only the important source code parts are shown. You can find the full open-source vector database implementation at the github repository.
A vector database is a specialized database optimized for storing and querying data in the form of high-dimensional vectors, often referred to as embeddings. These embeddings are numerical representations of data, such as text, images, or audio, created by machine learning models like MiniLM. Unlike traditional databases that work with exact matches on predefined fields, vector databases focus on semantic similarity, allowing you to query data based on meaning rather than exact values.
-
-
A vector, or embedding, is essentially an array of numbers, like [0.56, 0.12, -0.34, -0.90].
-
-
For example, instead of asking "Which document has the word 'database'?", you can query "Which documents discuss similar topics to this one?" The vector database compares embeddings and returns results based on how similar the vectors are to each other.
-
Vector databases handle multiple types of data beyond text, including images, videos, and audio files, all transformed into embeddings for efficient querying. Mostly you would not train a model by yourself and instead use one of the public available transformer models.
-
Vector databases are highly effective in various types of applications:
-
-
Similarity Search: Finds the closest matches to a query, even when the query doesn't contain the exact terms.
-
Clustering: Groups similar items based on the proximity of their vector representations.
-
Recommendations: Suggests items based on shared characteristics.
-
Anomaly Detection: Identifies outliers that differ from the norm.
-
Classification: Assigns categories to data based on its vector's nearest neighbors.
-
-
In this tutorial, we will build a vector database designed as a Similarity Search for text. For other use cases, the setup can be adapted accordingly. This flexibility is why RxDB doesn't provide a dedicated vector-database plugin, but rather offers utility functions to help you build your own vector search system.
For the first step to build a local-first vector database we need to compute embeddings directly on the user's device. This is where transformers.js from huggingface comes in, allowing us to run machine learning models in the browser with WebAssembly. Below is an implementation of a getEmbeddingFromText() function, which takes a piece of text and transforms it into an embedding using the Xenova/all-MiniLM-L6-v2 model:
This function creates an embedding by running the text through a pre-trained model and returning it in the form of an array of numbers, which can then be stored and further processed locally.
-
note
Vector embeddings from different machine learning models or versions are not compatible with each other. When you change your model, you have to recreate all embeddings for your data.
Also we need a vector collection that stores our embeddings.
-RxDB, as a NoSQL database, allows for the storage of flexible data structures, such as embeddings, within documents. To achieve this, we need to define a schema that specifies how the embeddings will be stored alongside each document. The schema includes fields for an id and the embedding array itself.
When storing documents in the database, we need to ensure that the embeddings for these documents are generated and stored automatically. This requires a handler that runs during every document write, calling the machine learning model to generate the embeddings and storing them in a separate vector collection.
-
Since our app runs in a browser, it's essential to avoid duplicate work when multiple browser tabs are open and ensure efficient use of resources. Furthermore, we want the app to resume processing documents from where it left off if it's closed or interrupted. To achieve this, RxDB provides a pipeline plugin, which allows us to set up a workflow that processes items and stores their embeddings. In our example, a pipeline takes batches of 10 documents, generates embeddings, and stores them in a separate vector collection.
However, processing data locally presents performance challenges. Running the handler with a batch size of 10 takes around 2-4 seconds per batch, meaning processing 10k documents would take up to an hour. To improve performance, we can do parallel processing using WebWorkers. A WebWorker runs on a different JavaScript process and we can start and run many of them in parallel.
-
Our worker listens for messages and performance the embedding generation on each request. It then sends the result embedding back to the main thread.
On the main thread we spawn one worker per core and send the tasks to the worker instead of processing them on the main thread.
-
// create one WebWorker per core
-const workers = new Array(navigator.hardwareConcurrency)
- .fill(0)
- .map(() => new Worker(new URL("worker.js", import.meta.url)));
This setup allows us to utilize the full hardware capacity of the client's machine. By setting the batch size to match the number of logical processors available (using the navigator.hardwareConcurrency API) and running one worker per processor, we can reduce the processing time for 10k embeddings to about 5 minutes on my developer laptop with 32 CPU cores.
Now that we have stored our embeddings in the database, the next step is to compare these vectors to each other. Various methods are available to measure the similarity or difference between two vectors, such as Euclidean distance, Manhattan distance, Cosine similarity, and Jaccard similarity (and more). RxDB provides utility functions for each of these methods, making it easy to choose the most suitable method for your application. In this tutorial, we will use Euclidean distance to compare vectors. However, the ideal algorithm may vary depending on your data's distribution and the specific type of query you are performing. To find the optimal method for your app, it is up to you to try out all of these and compare the results.
-
Each method gets two vectors as input and returns a single number. Here's how to calculate the Euclidean distance between two embeddings with the vector utilities from RxDB:
With this we can sort multiple embeddings by how good they match our search query vector.
-
Searching the Vector database with a full table scan
-
To find out if our embeddings have been stored correctly and that our vector comparison works as should, let's run a basic query to ensure everything functions as expected. In this query, we aim to find documents similar to a given user input text. The process involves calculating the embedding from the input text, fetching all documents, calculating the distance between their embeddings and the query embedding, and then sorting them based on their similarity.
For distance-based comparisons, sorting should be in ascending order (smallest first), while for similarity-based algorithms, the sorting should be in descending order (largest first).
-
If we inspect the results, we can see that the documents returned are ordered by relevance, with the most similar document at the top:
However our full-scan method presents a significant challenge: it does not scale well. As the number of stored documents increases, the time taken to fetch and compare embeddings grows proportionally. For example, retrieving embeddings from our test dataset of 10k documents takes around 700 milliseconds. If we scale up to 100k documents, this delay would rise to approximately 7 seconds, making the search process inefficient for larger datasets.
To address the scalability issue, we need to store embeddings in a way that allows us to avoid fetching all of them from storage during a query. In traditional databases, you can sort documents by an index field, allowing efficient queries that retrieve only the necessary documents. An index organizes data in a structured, sortable manner, much like a phone book. However, with vector embeddings we are not dealing with simple, single values. Instead, we have large lists of numbers, which makes indexing more complex because we have more than one dimension.
Various methods exist for indexing these vectors to improve query efficiency and performance:
-
-
Locality Sensitive Hashing (LSH): LSH hashes data so that similar items are likely to fall into the same bucket, optimizing approximate nearest neighbor searches in high-dimensional spaces by reducing the number of comparisons.
-
Hierarchical Small World: HSW is a graph structure designed for efficient navigation, allowing quick jumps across the graph while maintaining short paths between nodes, forming the basis for HNSW's optimization.
-
Hierarchical Navigable Small Worlds (HNSW): HNSW builds a hierarchical graph for fast approximate nearest neighbor search. It uses multiple layers where higher layers represent fewer, more connected nodes, improving search efficiency in large datasets.
-
Distance to samples: While testing different indexing strategies, I found out that using the distance to a sample set of items is a good way to index embeddings. You pick like 5 random items of your data and get the embeddings for them out of the model. These are your 5 index vectors. For each embedding stored in the vector database, we calculate the distance to our 5 index vectors and store that number as an index value. This seems to work good because similar things have similar distances to other things. For example the words "shoe" and "socks" have a similar distance to "boat" and therefore should have roughly the same index value.
-
-
When building local-first applications, performance is often a challenge, especially in JavaScript. With IndexedDB, certain operations, like many sequential get by id calls, are slow, while bulk operations, such as get by index range, are fast. Therefore, it's essential to use an indexing method that allows embeddings to be stored in a sortable way, like Locality Sensitive Hashing or Distance to Samples. In this article, we'll use Distance to Samples, because for me it provides the best default behavior for the sample dataset.
The optimal way to store index values alongside embeddings in RxDB is to place them within the same RxCollection. To ensure that the index values are both sortable and precise, we convert them into strings with a fixed length of 10 characters. This standardization helps in managing values with many decimals and ensures proper sorting in the database.
-
Here's is our schema example schema where each document contains an embedding and corresponding index fields:
To populate these index fields, we modify the RxPipeline handler accordingly to the Distance to samples method. We calculate the distance between the document's embedding and our set of 5 index vectors. The calculated distances are converted to string and stored in the appropriate index fields:
-
import { euclideanDistance } from 'rxdb/plugins/vector';
-const sampleVectors: number[][] = [/* the index vectors */];
-const pipeline = await itemsCollection.addPipeline({
- handler: async (docs) => {
- await Promise.all(docs.map(async(doc) => {
- const embedding = await getEmbedding(doc.text);
- const docData = { id: doc.primary, embedding };
- // calculate the distance to all samples and store them in the index fields
- new Array(5).fill(0).map((_, idx) => {
- const indexValue = euclideanDistance(sampleVectors[idx], embedding);
- docData['idx' + idx] = indexNrToString(indexValue);
- });
- await vectorCollection.upsert(docData);
- }));
- }
-});
-
Searching the Vector database with utilization of the indexes
-
Once our embeddings are stored in an indexed format, we can perform searches much more efficiently than through a full table scan. While this indexing method boosts performance, it comes with a tradeoff: a slight loss in precision, meaning that the result set may not always be the optimal one. However, this is generally acceptable for similarity search use cases.
-
There are multiple ways to leverage indexes for faster queries. Here are two effective methods:
-
-
Query for Index Similarity in Both Directions: For each index vector, calculate the distance to the search embedding and fetch all relevant embeddings in both directions (sorted before and after) from that value.
Query for an Index Range with a Defined Distance: Set an indexDistance and retrieve all embeddings within a specified range from the index vector to the search embedding.
Both methods allow you to limit the number of embeddings fetched from storage while still ensuring a reasonably precise search result. However, they differ in how many embeddings are read and how precise the results are, with trade-offs between performance and accuracy. The first method has a known embedding read amount of docsPerIndexSide * 2 * [amount of indexes]. The second method reads out an unknown amount of embeddings, depending on the sparsity of the dataset and the value of indexDistance.
-
And that's it for the implementation. We now have a local first vector database that is able to store and query vector data.
In server-side databases, performance can be improved by scaling hardware or adding more servers. However, local-first apps face the unique challenge that the hardware is determined by the end user, making performance unpredictable. Some users may have high-end gaming PCs, while others might be using outdated smartphones in power-saving mode. Therefore, when building a local-first app that processes more than a few documents, performance becomes a critical factor and should be thoroughly tested upfront.
-
Let's run performance benchmarks on my high-end gaming PC to give you a sense of how long different operations take and what's achievable.
As shown, the index similarity query method takes significantly longer compared to others. This is due to the need for descending sort orders in some queries sort: [{ ['idx' + i]: 'desc' }]. While RxDB supports descending sorts, performance suffers because IndexedDB does not efficiently handle reverse indexed bulk operations. As a result, the index range method performs much better for this use case and should be used instead. With its query time of only 88 milliseconds it is fast enough for all most things and likely such fast that you do not even need to show a loading spinner. Also it is faster compared to fetching the query result from a server-side vector database over the internet.
Let's also look at the time taken to calculate a single embedding across various models from the huggingface transformers list:
-
Model Name
Time per Embedding in (ms)
Vector Size
Model Size (MB)
Xenova/all-MiniLM-L6-v2
173
384
23
Supabase/gte-small
341
384
34
Xenova/paraphrase-multilingual-mpnet-base-v2
1000
768
279
jinaai/jina-embeddings-v2-base-de
1291
768
162
jinaai/jina-embeddings-v2-base-zh
1437
768
162
jinaai/jina-embeddings-v2-base-code
1769
768
162
mixedbread-ai/mxbai-embed-large-v1
3359
1024
337
WhereIsAI/UAE-Large-V1
3499
1024
337
Xenova/multilingual-e5-large
4215
1024
562
-
From these benchmarks, it's evident that models with larger vector outputs take longer to process. Additionally, the model size significantly affects performance, with larger models requiring more time to compute embeddings. This trade-off between model complexity and performance must be considered when choosing the right model for your use case.
There are multiple other techniques to improve the performance of your local vector database:
-
-
-
Shorten embeddings: The storing and retrieval of embeddings can be improved by "shortening" the embedding. To do that, you just strip away numbers from your vector. For example [0.56, 0.12, -0.34, 0.78, -0.90] becomes [0.56, 0.12]. That's it, you now have a smaller embedding that is faster to read out of the storage and calculating distances is faster because it has to process less numbers. The downside is that you loose precision in your search results. Sometimes shortening the embeddings makes more sense as a pre-query step where you first compare the shortened vectors and later fetch the "real" vectors for the 10 most matching documents to improve their sort order.
-
-
-
Optimize the variables in our Setup: In this examples we picked our variables in a non-optimal way. You can get huge performance improvements by setting different values:
-
-
We picked 5 indexes for the embeddings. Using less indexes improves your query performance with the cost of less good results.
-
For queries that search by fetching a specific embedding distance we used the indexDistance value of 0.003. Using a lower value means we read less document from the storage. This is faster but reduces the precision of the results which means we will get a less optimal result compared to a full table scan.
-
For queries that search by fetching a given amount of documents per index side, we set the value docsPerIndexSide to 100. Increasing this value means you fetch more data from the storage but also get a better precision in the search results. Decreasing it can improve query performance with worse precision.
-
-
-
-
Use faster models: There are many ways to improve performance of machine learning models. If your embedding calculation is too slow, try other models. Smaller mostly means faster. The model Xenova/all-MiniLM-L6-v2 which is used in this tutorial is about 1 year old. There exist better, more modern models to use. Huggingface makes these convenient to use. You only have to switch out the model name with any other model from that site.
-
-
-
Narrow down the search space: By utilizing other "normal" filter operators to your query, you can narrow down the search space and optimize performance. For example in an email search you could additionally use a operator that limits the results to all emails that are not older than one year.
-
-
-
Dimensionality Reduction with an autoencoder: An autoencoder encodes vector data with minimal loss which can improve the performance by having to store and compare less numbers in an embedding.
When you change the index parameter or even update the whole model which was used to create the embeddings, you have to migrate the data that is already stored on your users devices. RxDB offers the Schema Migration Plugin for that.
-
When the app is reloaded and the updated source code is started, RxDB detects changes in your schema version and runs the migration strategy accordingly. So to update the stored data, increase the schema version and define a handler:
Possible Future Improvements to Local-First Vector Databases
-
For now our vector database works and we are good to go. However there are some things to consider for the future:
-
-
WebGPU is not fully supported yet. When this changes, creating embeddings in the browser have the potential to become faster. You can check if your current chrome supports WebGPU by opening chrome://gpu/. Notice that WebGPU has been reported to sometimes be even slower compared to WASM but likely it will be faster in the long term.
-
Cross-Modal AI Models: While progress is being made, AI models that can understand and integrate multiple modalities are still in development. For example you could query for an image together with a text prompt to get a more detailed output.
-
Multi-Step queries: In this article we only talked about having a single query as input and an ordered list of outputs. But there is big potential in chaining models or queries together where you take the results of one query and input them into a different model with different embeddings or outputs.
-
-
\ No newline at end of file
diff --git a/docs/articles/javascript-vector-database.md b/docs/articles/javascript-vector-database.md
deleted file mode 100644
index cdd0933f022..00000000000
--- a/docs/articles/javascript-vector-database.md
+++ /dev/null
@@ -1,592 +0,0 @@
-# Local JavaScript Vector Database that works offline
-
-> Create a blazing-fast vector database in JavaScript. Leverage RxDB and transformers.js for instant, offline semantic search - no servers required!
-
-# Local Vector Database with RxDB and transformers.js in JavaScript
-
-The [local-first](../offline-first.md) revolution is here, changing the way we build apps! Imagine a world where your app's data lives right on the user's device, always available, even when there's no internet. That's the magic of local-first apps. Not only do they bring faster performance and limitless scalability, but they also empower users to work offline without missing a beat. And leading the charge in this space are local database solutions, like [RxDB](https://rxdb.info/).
-
-
-
-
-
-
-
-But here's where things get even more exciting: when building [local-first](./local-first-future.md) apps, traditional databases often fall short. They're great at searching for exact matches, like `numbers` or `strings`, but what if you want to search by **meaning**, like sifting through emails to find a specific topic? Sure, you could use **RegExp**, but to truly unlock the power of semantic search and similarity-based queries, you need something more cutting-edge. Something that really understands the content of the data.
-
-Enter **vector databases**, the game-changers for searching data by meaning! They have unlocked these new possibilities for storing and querying data, especially in tasks requiring **semantic search** and **similarity-based** queries. With the help of a **machine learning model**, data is transformed into a vector representation that can be stored, queried and compared in a database.
-
-But unfortunately, most vector databases are designed for server-side use, typically running in large cloud clusters, not to run on a users device. To fix that, in this article, we will combine **RxDB** and **transformers.js** to create a local vector database running in the **browser** with **JavaScript**. It stores data in **IndexedDB**, and uses a machine learning model with **WebAssembly** locally, without the need for external servers.
-
-- [transformers.js](https://github.com/xenova/transformers.js) is a powerful framework that allows machine learning models to run directly within JavaScript using WebAssembly or WebGPU.
-
-- [RxDB](https://rxdb.info/) is a NoSQL, local-first database with a flexible storage layer that can run on any JavaScript runtime, including browsers and mobile environments. (You are reading this article on the RxDB docs).
-
-A local vector database offers several key benefits:
-
-- **Zero network latency**: Data is processed locally on the user's device, ensuring near-instant responses.
-- **Offline functionality**: Data can be queried even without an internet connection.
-- **Enhanced privacy**: Sensitive information remains on the device, never needing to leave for external processing.
-- **Simple setup**: No backend servers are required, making deployment straightforward.
-- **Cost savings**: By running everything locally, you avoid fees for API access or cloud services for large language models.
-
-:::note
-In this article only the important source code parts are shown. You can find the full open-source vector database implementation at the [github repository](https://github.com/pubkey/javascript-vector-database).
-:::
-
-## What is a Vector Database?
-
-A vector database is a specialized database optimized for storing and querying data in the form of **high-dimensional** vectors, often referred to as **embeddings**. These embeddings are numerical representations of data, such as text, images, or audio, created by machine learning models like [MiniLM](https://huggingface.co/Xenova/all-MiniLM-L6-v2). Unlike traditional databases that work with exact matches on predefined fields, vector databases focus on **semantic similarity**, allowing you to query data based on meaning rather than exact values.
-
-> A vector, or embedding, is essentially an array of numbers, like `[0.56, 0.12, -0.34, -0.90]`.
-
-For example, instead of asking "Which document has the word 'database'?", you can query "Which documents discuss similar topics to this one?" The vector database compares embeddings and returns results based on how similar the vectors are to each other.
-
-Vector databases handle multiple types of data beyond **text**, including **images**, **videos**, and **audio** files, all transformed into embeddings for efficient querying. Mostly you would not train a model by yourself and instead use one of the public available [transformer models](https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js).
-
-Vector databases are highly effective in various types of applications:
-
-- **Similarity Search**: Finds the closest matches to a query, even when the query doesn't contain the exact terms.
-- **Clustering**: Groups similar items based on the proximity of their vector representations.
-- **Recommendations**: Suggests items based on shared characteristics.
-- **Anomaly Detection**: Identifies outliers that differ from the norm.
-- **Classification**: Assigns categories to data based on its vector's nearest neighbors.
-
-In this tutorial, we will build a vector database designed as a **Similarity Search** for **text**. For other use cases, the setup can be adapted accordingly. This flexibility is why [RxDB](https://rxdb.info/) doesn't provide a dedicated vector-database plugin, but rather offers utility functions to help you build your own vector search system.
-
-
-
-
-
-## Generating Embeddings Locally in a Browser
-
-For the first step to build a local-first vector database we need to compute embeddings directly on the user's device. This is where [transformers.js](https://github.com/xenova/transformers.js) from [huggingface](https://huggingface.co/docs/transformers.js/index) comes in, allowing us to run machine learning models in the browser with **WebAssembly**. Below is an implementation of a `getEmbeddingFromText()` function, which takes a piece of text and transforms it into an embedding using the [Xenova/all-MiniLM-L6-v2](https://huggingface.co/Xenova/all-MiniLM-L6-v2) model:
-
-```js
-import { pipeline } from "@xenova/transformers";
-const pipePromise = pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
-async function getEmbeddingFromText(text) {
- const pipe = await pipePromise;
- const output = await pipe(text, {
- pooling: "mean",
- normalize: true,
- });
- return Array.from(output.data);
-}
-```
-
-This function creates an embedding by running the text through a pre-trained model and returning it in the form of an array of numbers, which can then be stored and further processed locally.
-
-:::note
-Vector embeddings from different machine learning models or versions are not compatible with each other. When you change your model, you have to recreate all embeddings for your data.
-:::
-
-## Storing the Embeddings in RxDB
-
-To store the embeddings, first we have to create our [RxDB Database](../rx-database.md) with the [localstorage storage](../rx-storage-localstorage.md) that stores data in the browsers [localstorage](./localstorage.md). For more advanced projects, you can use any other [RxStorage](../rx-storage.md).
-
-```ts
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-const db = await createRxDatabase({
- name: 'mydatabase',
- storage: getRxStorageLocalstorage()
-});
-```
-
-Then we add a `items` collection that stores our documents with the `text` field that stores the content.
-
-```ts
-await db.addCollections({
- items: {
- schema: {
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 20
- },
- text: {
- type: 'string'
- }
- },
- required: ['id', 'text']
- }
- }
-});
-const itemsCollection = db.items;
-```
-
-In our [example repo](https://github.com/pubkey/javascript-vector-database), we use the [Wiki Embeddings](https://huggingface.co/datasets/Supabase/wikipedia-en-embeddings) dataset from supabase which was transformed and used to fill up the `items` collection with test data.
-
-```ts
-const imported = await itemsCollection.count().exec();
-const response = await fetch('./files/items.json');
-const items = await response.json();
-const insertResult = await itemsCollection.bulkInsert(
- items
-);
-```
-
-Also we need a `vector` collection that stores our embeddings.
-RxDB, as a NoSQL database, allows for the storage of flexible data structures, such as embeddings, within documents. To achieve this, we need to define a [schema](../rx-schema.md) that specifies how the embeddings will be stored alongside each document. The schema includes fields for an `id` and the `embedding` array itself.
-
-```ts
-await db.addCollections({
- vector: {
- schema: {
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 20
- },
- embedding: {
- type: 'array',
- items: {
- type: 'string'
- }
- }
- },
- required: ['id', 'embedding']
- }
- }
-});
-const vectorCollection = db.vector;
-```
-
-When storing documents in the database, we need to ensure that the embeddings for these documents are generated and stored automatically. This requires a handler that runs during every document write, calling the machine learning model to generate the embeddings and storing them in a separate vector collection.
-
-Since our app runs in a browser, it's essential to avoid duplicate work when **multiple browser tabs** are open and ensure efficient use of resources. Furthermore, we want the app to resume processing documents from where it left off if it's closed or interrupted. To achieve this, RxDB provides a [pipeline plugin](../rx-pipeline.md), which allows us to set up a workflow that processes items and stores their embeddings. In our example, a pipeline takes batches of 10 documents, generates embeddings, and stores them in a separate vector collection.
-
-```ts
-const pipeline = await itemsCollection.addPipeline({
- identifier: 'my-embeddings-pipeline',
- destination: vectorCollection,
- batchSize: 10,
- handler: async (docs) => {
- await Promise.all(docs.map(async(doc) => {
- const embedding = await getVectorFromText(doc.text);
- await vectorCollection.upsert({
- id: doc.primary,
- embedding
- });
- }));
- }
-});
-```
-
-However, processing data locally presents performance challenges. Running the handler with a batch size of 10 takes around **2-4 seconds per batch**, meaning processing 10k documents would take up to an hour. To improve performance, we can do parallel processing using [WebWorkers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers). A WebWorker runs on a different JavaScript process and we can start and run many of them in parallel.
-
-Our worker listens for messages and performance the embedding generation on each request. It then sends the result embedding back to the main thread.
-
-```ts
-// worker.js
-import { getVectorFromText } from './vector.js';
-onmessage = async (e) => {
- const embedding = await getVectorFromText(e.data.text);
- postMessage({
- id: e.data.id,
- embedding
- });
-};
-```
-
-On the main thread we spawn one worker per core and send the tasks to the worker instead of processing them on the main thread.
-
-```ts
-// create one WebWorker per core
-const workers = new Array(navigator.hardwareConcurrency)
- .fill(0)
- .map(() => new Worker(new URL("worker.js", import.meta.url)));
-```
-
-```ts
-let lastWorkerId = 0;
-let lastId = 0;
-export async function getVectorFromTextWithWorker(text: string): Promise {
- let worker = workers[lastWorkerId++];
- if(!worker) {
- lastWorkerId = 0;
- worker = workers[lastWorkerId++];
- }
- const id = (lastId++) + '';
- return new Promise(res => {
- const listener = (ev: any) => {
- if (ev.data.id === id) {
- res(ev.data.embedding);
- worker.removeEventListener('message', listener);
- }
- };
- worker.addEventListener('message', listener);
- worker.postMessage({
- id,
- text
- });
- });
-}
-
-const pipeline = await itemsCollection.addPipeline({
- identifier: 'my-embeddings-pipeline',
- destination: vectorCollection,
- batchSize: navigator.hardwareConcurrency, // one per CPU core
- handler: async (docs) => {
- await Promise.all(docs.map(async (doc, i) => {
- const embedding = await getVectorFromTextWithWorker(doc.body);
- /* ... */
- });
- }
-});
-```
-
-This setup allows us to utilize the full hardware capacity of the client's machine. By setting the batch size to match the number of logical processors available (using the [navigator.hardwareConcurrency](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) API) and running one worker per processor, we can reduce the processing time for 10k embeddings to **about 5 minutes** on my developer laptop with 32 CPU cores.
-
-## Comparing Vectors by calculating the distance
-
-Now that we have stored our embeddings in the database, the next step is to compare these vectors to each other. Various methods are available to measure the similarity or difference between two vectors, such as [Euclidean distance](https://en.wikipedia.org/wiki/Euclidean_distance), [Manhattan distance](https://www.singlestore.com/blog/distance-metrics-in-machine-learning-simplfied/), [Cosine similarity](https://tomhazledine.com/cosine-similarity/), and **Jaccard similarity** (and more). RxDB provides utility functions for each of these methods, making it easy to choose the most suitable method for your application. In this tutorial, we will use **Euclidean distance** to compare vectors. However, the ideal algorithm may vary depending on your data's distribution and the specific type of query you are performing. To find the optimal method for your app, it is up to you to try out all of these and compare the results.
-
-Each method gets two vectors as input and returns a single number. Here's how to calculate the Euclidean distance between two embeddings with the vector utilities from RxDB:
-
-```ts
-import { euclideanDistance } from 'rxdb/plugins/vector';
-const distance = euclideanDistance(embedding1, embedding2);
-console.log(distance); // 25.20443
-```
-
-With this we can sort multiple embeddings by how good they match our search query vector.
-
-## Searching the Vector database with a full table scan
-
-To find out if our embeddings have been stored correctly and that our vector comparison works as should, let's run a basic query to ensure everything functions as expected. In this query, we aim to find documents similar to a given user input text. The process involves calculating the embedding from the input text, fetching all documents, calculating the distance between their embeddings and the query embedding, and then sorting them based on their similarity.
-
-```ts
-import { euclideanDistance } from 'rxdb/plugins/vector';
-import { sortByObjectNumberProperty } from 'rxdb/plugins/core';
-
-const userInput = 'new york people';
-const queryVector = await getEmbeddingFromText(userInput);
-const candidates = await vectorCollection.find().exec();
-const withDistance = candidates.map(doc => ({
- doc,
- distance: euclideanDistance(queryVector, doc.embedding)
-}));
-const queryResult = withDistance.sort(sortByObjectNumberProperty('distance')).reverse();
-console.dir(queryResult);
-```
-
-:::note
-For **distance**-based comparisons, sorting should be in ascending order (smallest first), while for **similarity**-based algorithms, the sorting should be in descending order (largest first).
-:::
-
-If we inspect the results, we can see that the documents returned are ordered by relevance, with the most similar document at the top:
-
-
-
-
-
-:::note
-This demo page can be [run online here](https://pubkey.github.io/javascript-vector-database/).
-:::
-
-However our full-scan method presents a significant challenge: it does not scale well. As the number of stored documents increases, the time taken to fetch and compare embeddings grows proportionally. For example, retrieving embeddings from our [test dataset](https://huggingface.co/datasets/Supabase/wikipedia-en-embeddings) of 10k documents takes around **700 milliseconds**. If we scale up to 100k documents, this delay would rise to approximately **7 seconds**, making the search process inefficient for larger datasets.
-
-## Indexing the Embeddings for Better Performance
-
-To address the scalability issue, we need to store embeddings in a way that allows us to avoid fetching all of them from storage during a query. In traditional databases, you can sort documents by an **index field**, allowing efficient queries that retrieve only the necessary documents. An index organizes data in a structured, sortable manner, much **like a phone book**. However, with vector embeddings we are not dealing with simple, single values. Instead, we have large **lists of numbers**, which makes indexing more complex because we have more than one dimension.
-
-### Vector Indexing Methods
-
-Various methods exist for indexing these vectors to improve query efficiency and performance:
-
-- [Locality Sensitive Hashing (LSH)](https://www.youtube.com/watch?v=Arni-zkqMBA): LSH hashes data so that similar items are likely to fall into the same bucket, optimizing approximate nearest neighbor searches in high-dimensional spaces by reducing the number of comparisons.
-- [Hierarchical Small World](https://www.youtube.com/watch?v=77QH0Y2PYKg): HSW is a graph structure designed for efficient navigation, allowing quick jumps across the graph while maintaining short paths between nodes, forming the basis for HNSW's optimization.
-- [Hierarchical Navigable Small Worlds (HNSW)](https://www.youtube.com/watch?v=77QH0Y2PYKg): HNSW builds a hierarchical graph for fast approximate nearest neighbor search. It uses multiple layers where higher layers represent fewer, more connected nodes, improving search efficiency in large datasets.
-- **Distance to samples**: While testing different indexing strategies, [I](https://github.com/pubkey) found out that using the distance to a sample set of items is a good way to index embeddings. You pick like 5 random items of your data and get the embeddings for them out of the model. These are your 5 index vectors. For each embedding stored in the vector database, we calculate the distance to our 5 index vectors and store that `number` as an index value. This seems to work good because similar things have similar distances to other things. For example the words "shoe" and "socks" have a similar distance to "boat" and therefore should have roughly the same index value.
-
-When building **local-first** applications, performance is often a challenge, especially in JavaScript. With **IndexedDB**, certain operations, like many sequential `get by id` calls, [are slow](../slow-indexeddb.md), while bulk operations, such as `get by index range`, are fast. Therefore, it's essential to use an indexing method that allows embeddings to be stored in a sortable way, like **Locality Sensitive Hashing** or **Distance to Samples**. In this article, we'll use **Distance to Samples**, because for [me](https://github.com/pubkey) it provides the best default behavior for the sample dataset.
-
-### Storing indexed embeddings in RxDB
-
-The optimal way to store index values alongside embeddings in RxDB is to place them within the same [RxCollection](../rx-collection.md). To ensure that the index values are both sortable and precise, we convert them into strings with a fixed length of `10` characters. This standardization helps in managing values with many decimals and ensures proper sorting in the database.
-
-Here's is our schema example schema where each document contains an embedding and corresponding index fields:
-
-```ts
-const indexSchema = {
- type: 'string',
- maxLength: 10
-};
-const schema = {
- "version": 0,
- "primaryKey": "id",
- "type": "object",
- "properties": {
- "id": {
- "type": "string",
- "maxLength": 100
- },
- "embedding": {
- "type": "array",
- "items": {
- "type": "number"
- }
- },
- // index fields
- "idx0": indexSchema,
- "idx1": indexSchema,
- "idx2": indexSchema,
- "idx3": indexSchema,
- "idx4": indexSchema
- },
- "required": [
- "id",
- "embedding",
- "idx0",
- "idx1",
- "idx2",
- "idx3",
- "idx4"
- ],
- "indexes": [
- "idx0",
- "idx1",
- "idx2",
- "idx3",
- "idx4"
- ]
-}
-```
-
-To populate these index fields, we modify the [RxPipeline](../rx-pipeline.md) handler accordingly to the **Distance to samples** method. We calculate the distance between the document's embedding and our set of `5` index vectors. The calculated distances are converted to `string` and stored in the appropriate index fields:
-
-```ts
-import { euclideanDistance } from 'rxdb/plugins/vector';
-const sampleVectors: number[][] = [/* the index vectors */];
-const pipeline = await itemsCollection.addPipeline({
- handler: async (docs) => {
- await Promise.all(docs.map(async(doc) => {
- const embedding = await getEmbedding(doc.text);
- const docData = { id: doc.primary, embedding };
- // calculate the distance to all samples and store them in the index fields
- new Array(5).fill(0).map((_, idx) => {
- const indexValue = euclideanDistance(sampleVectors[idx], embedding);
- docData['idx' + idx] = indexNrToString(indexValue);
- });
- await vectorCollection.upsert(docData);
- }));
- }
-});
-```
-
-## Searching the Vector database with utilization of the indexes
-
-Once our embeddings are stored in an indexed format, we can perform searches much **more efficiently** than through a full table scan. While this indexing method boosts performance, it comes with a tradeoff: a slight loss in precision, meaning that the result set may not always be the optimal one. However, this is generally acceptable for **similarity search** use cases.
-
-There are multiple ways to leverage indexes for faster queries. Here are two effective methods:
-
-1. **Query for Index Similarity in Both Directions**: For each index vector, calculate the distance to the search embedding and fetch all relevant embeddings in both directions (sorted before and after) from that value.
-
-```ts
-async function vectorSearchIndexSimilarity(searchEmbedding: number[]) {
- const docsPerIndexSide = 100;
- const candidates = new Set();
- await Promise.all(
- new Array(5).fill(0).map(async (_, i) => {
- const distanceToIndex = euclideanDistance(sampleVectors[i], searchEmbedding);
- const [docsBefore, docsAfter] = await Promise.all([
- vectorCollection.find({
- selector: {
- ['idx' + i]: {
- $lt: indexNrToString(distanceToIndex)
- }
- },
- sort: [{ ['idx' + i]: 'desc' }],
- limit: docsPerIndexSide
- }).exec(),
- vectorCollection.find({
- selector: {
- ['idx' + i]: {
- $gt: indexNrToString(distanceToIndex)
- }
- },
- sort: [{ ['idx' + i]: 'asc' }],
- limit: docsPerIndexSide
- }).exec()
- ]);
- docsBefore.map(d => candidates.add(d));
- docsAfter.map(d => candidates.add(d));
- })
- );
- const docsWithDistance = Array.from(candidates).map(doc => {
- const distance = euclideanDistance((doc as any).embedding, searchEmbedding);
- return {
- distance,
- doc
- };
- });
- const sorted = docsWithDistance.sort(sortByObjectNumberProperty('distance')).reverse();
- return {
- result: sorted.slice(0, 10),
- docReads
- };
-}
-```
-
-2. **Query for an Index Range with a Defined Distance**: Set an `indexDistance` and retrieve all embeddings within a specified range from the index vector to the search embedding.
-
-```ts
-async function vectorSearchIndexRange(searchEmbedding: number[]) {
- await pipeline.awaitIdle();
- const indexDistance = 0.003;
- const candidates = new Set();
- let docReads = 0;
- await Promise.all(
- new Array(5).fill(0).map(async (_, i) => {
- const distanceToIndex = euclideanDistance(sampleVectors[i], searchEmbedding);
- const range = distanceToIndex * indexDistance;
- const docs = await vectorCollection.find({
- selector: {
- ['idx' + i]: {
- $gt: indexNrToString(distanceToIndex - range),
- $lt: indexNrToString(distanceToIndex + range)
- }
- },
- sort: [{ ['idx' + i]: 'asc' }],
- }).exec();
- docs.map(d => candidates.add(d));
- docReads = docReads + docs.length;
- })
- );
-
- const docsWithDistance = Array.from(candidates).map(doc => {
- const distance = euclideanDistance((doc as any).embedding, searchEmbedding);
- return {
- distance,
- doc
- };
- });
- const sorted = docsWithDistance.sort(sortByObjectNumberProperty('distance')).reverse();
- return {
- result: sorted.slice(0, 10),
- docReads
- };
-};
-```
-
-Both methods allow you to limit the number of embeddings fetched from storage while still ensuring a reasonably precise search result. However, they differ in how many embeddings are read and how precise the results are, with trade-offs between performance and accuracy. The first method has a known embedding read amount of `docsPerIndexSide * 2 * [amount of indexes]`. The second method reads out an unknown amount of embeddings, depending on the sparsity of the dataset and the value of `indexDistance`.
-
-And that's it for the implementation. We now have a local first vector database that is able to store and query vector data.
-
-## Performance benchmarks
-
-In server-side databases, performance can be improved by scaling hardware or adding more servers. However, [local-first](../offline-first.md) apps face the unique challenge that the hardware is determined by the end user, making performance unpredictable. Some users may have **high-end gaming PCs**, while others might be using **outdated smartphones in power-saving mode**. Therefore, when building a local-first app that processes more than a few documents, performance becomes a critical factor and should be thoroughly tested upfront.
-
-Let's run performance benchmarks on my **high-end gaming PC** to give you a sense of how long different operations take and what's achievable.
-
-### Performance of the Query Methods
-
-| Query Method | Time in milliseconds | Docs read from storage |
-| ---------------- | -------------------- | ---------------------- |
-| Full Scan | 765 | 10000 |
-| Index Similarity | 1647 | 934 |
-| Index Range | 88 | 2187 |
-
-As shown, the **index similarity** query method takes significantly longer compared to others. This is due to the need for descending sort orders in some queries `sort: [{ ['idx' + i]: 'desc' }]`. While RxDB supports descending sorts, performance suffers because IndexedDB does not efficiently handle [reverse indexed bulk operations](https://github.com/w3c/IndexedDB/issues/130). As a result, the **index range method** performs much better for this use case and should be used instead. With its query time of only `88` milliseconds it is fast enough for all most things and likely such fast that you do not even need to show a loading spinner. Also it is faster compared to fetching the query result from a server-side vector database over the internet.
-
-### Performance of the Models
-
-Let's also look at the time taken to calculate a single embedding across various models from the [huggingface transformers list](https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js):
-
-| Model Name | Time per Embedding in (ms) | Vector Size | Model Size (MB) |
-| -------------------------------------------- | -------------------------- | ----------- | --------------- |
-| Xenova/all-MiniLM-L6-v2 | 173 | 384 | 23 |
-| Supabase/gte-small | 341 | 384 | 34 |
-| Xenova/paraphrase-multilingual-mpnet-base-v2 | 1000 | 768 | 279 |
-| jinaai/jina-embeddings-v2-base-de | 1291 | 768 | 162 |
-| jinaai/jina-embeddings-v2-base-zh | 1437 | 768 | 162 |
-| jinaai/jina-embeddings-v2-base-code | 1769 | 768 | 162 |
-| mixedbread-ai/mxbai-embed-large-v1 | 3359 | 1024 | 337 |
-| WhereIsAI/UAE-Large-V1 | 3499 | 1024 | 337 |
-| Xenova/multilingual-e5-large | 4215 | 1024 | 562 |
-
-From these benchmarks, it's evident that models with larger vector outputs **take longer to process**. Additionally, the model size significantly affects performance, with larger models requiring more time to compute embeddings. This trade-off between model complexity and performance must be considered when choosing the right model for your use case.
-
-## Potential Performance Optimizations
-
-There are multiple other techniques to improve the performance of your local vector database:
-
-- **Shorten embeddings**: The storing and retrieval of embeddings can be improved by "shortening" the embedding. To do that, you just strip away numbers from your vector. For example `[0.56, 0.12, -0.34, 0.78, -0.90]` becomes `[0.56, 0.12]`. That's it, you now have a smaller embedding that is faster to read out of the storage and calculating distances is faster because it has to process less numbers. The downside is that you loose precision in your search results. Sometimes shortening the embeddings makes more sense as a pre-query step where you first compare the shortened vectors and later fetch the "real" vectors for the 10 most matching documents to improve their sort order.
-
-- **Optimize the variables in our Setup**: In this examples we picked our variables in a non-optimal way. You can get huge performance improvements by setting different values:
- - We picked 5 indexes for the embeddings. Using less indexes improves your query performance with the cost of less good results.
- - For queries that search by fetching a specific embedding distance we used the `indexDistance` value of `0.003`. Using a lower value means we read less document from the storage. This is faster but reduces the precision of the results which means we will get a less optimal result compared to a full table scan.
- - For queries that search by fetching a given amount of documents per index side, we set the value `docsPerIndexSide` to `100`. Increasing this value means you fetch more data from the storage but also get a better precision in the search results. Decreasing it can improve query performance with worse precision.
-
-- **Use faster models**: There are many ways to improve performance of machine learning models. If your embedding calculation is too slow, try other models. **Smaller** mostly means **faster**. The model `Xenova/all-MiniLM-L6-v2` which is used in this tutorial is about [1 year old](https://huggingface.co/Xenova/all-MiniLM-L6-v2/tree/main). There exist better, more modern models to use. Huggingface makes these convenient to use. You only have to switch out the model name with any other model from [that site](https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js).
-
-- **Narrow down the search space**: By utilizing other "normal" filter operators to your query, you can narrow down the search space and optimize performance. For example in an email search you could additionally use a operator that limits the results to all emails that are not older than one year.
-
-- **Dimensionality Reduction** with an [autoencoder](https://www.youtube.com/watch?v=D16rii8Azuw): An autoencoder encodes vector data with minimal loss which can improve the performance by having to store and compare less numbers in an embedding.
-
-- **Different RxDB Plugins**: RxDB has different storages and plugins that can improve the performance like the [IndexedDB RxStorage](../rx-storage-indexeddb.md), the [OPFS RxStorage](../rx-storage-opfs.md), the [sharding](../rx-storage-sharding.md) plugin and the [Worker](../rx-storage-worker.md) and [SharedWorker](../rx-storage-shared-worker.md) storages.
-
-
-
-
-
-
-
-## Migrating Data on Model/Index Changes
-
-When you change the index parameter or even update the whole model which was used to create the embeddings, you have to migrate the data that is already stored on your users devices. RxDB offers the [Schema Migration Plugin](../migration-schema.md) for that.
-
-When the app is reloaded and the updated source code is started, RxDB detects changes in your [schema version](../rx-schema.md#version) and runs the [migration strategy](../migration-schema.md#providing-strategies) accordingly. So to update the stored data, increase the schema version and define a handler:
-
-```ts
-const schemaV1 = {
- "version": 1, // <- increase schema version by 1
- "primaryKey": "id",
- "properties": {
- /* ... */
- },
- /* ... */
-};
-```
-
-In the migration handler we recreate the new embeddings and index values.
-
-```ts
-await myDatabase.addCollections({
- vectors: {
- schema: schemaV1,
- migrationStrategies: {
- 1: function(docData){
- const embedding = await getEmbedding(docData.body);
- new Array(5).fill(0).map((_, idx) => {
- docData['idx' + idx] = euclideanDistance(mySampleVectors[idx], embedding);
- });
- return docData;
- },
- }
- }
-});
-```
-
-## Possible Future Improvements to Local-First Vector Databases
-
-For now our vector database works and we are good to go. However there are some things to consider for the future:
-
-- **WebGPU** is [not fully supported](https://caniuse.com/webgpu) yet. When this changes, creating embeddings in the browser have the potential to become faster. You can check if your current chrome supports WebGPU by opening `chrome://gpu/`. Notice that WebGPU has been reported to sometimes be [even slower](https://github.com/xenova/transformers.js/issues/894#issuecomment-2323897485) compared to WASM but likely it will be faster in the long term.
-- **Cross-Modal AI Models**: While progress is being made, AI models that can understand and integrate multiple modalities are still in development. For example you could query for an **image** together with a **text** prompt to get a more detailed output.
-- **Multi-Step queries**: In this article we only talked about having a single query as input and an ordered list of outputs. But there is big potential in chaining models or queries together where you take the results of one query and input them into a different model with different embeddings or outputs.
-
-## Follow Up
-- Shared/Like my [announcement tweet](https://x.com/rxdbjs/status/1833429569434427494)
-- Read the source code that belongs to this article [at github](https://github.com/pubkey/javascript-vector-database)
-- Learn how to use RxDB with the [RxDB Quickstart](../quickstart.md)
-- Check out the [RxDB github repo](https://github.com/pubkey/rxdb) and leave a star ⭐
diff --git a/docs/articles/jquery-database.html b/docs/articles/jquery-database.html
deleted file mode 100644
index c5b79c40a60..00000000000
--- a/docs/articles/jquery-database.html
+++ /dev/null
@@ -1,194 +0,0 @@
-
-
-
-
-
-RxDB as a Database in a jQuery Application | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In the early days of dynamic web development, jQuery emerged as a popular library that simplified DOM manipulation and AJAX requests. Despite the rise of modern frameworks, many developers still maintain or extend existing jQuery projects, or leverage jQuery in specific contexts. As jQuery applications grow in complexity, they often require efficient data handling, offline support, and synchronization capabilities. This is where RxDB, a reactive JavaScript database for the browser, node.js, and mobile devices, steps in.
jQuery provides a simple API for DOM manipulation, event handling, and AJAX calls. It has been widely adopted due to its ease of use and strong community support. Many projects continue to rely on jQuery for handling client-side functionality, UI interactions, and animations. As these applications evolve, the need for a robust database solution that can manage data locally (and offline) becomes increasingly important.
Modern, data-driven jQuery applications often need to:
-
-
Store and retrieve data locally for quick and responsive user experiences.
-
Synchronize data between clients or with a central server.
-
Handle offline scenarios seamlessly.
-
Handle large or complex data structures without repeatedly hitting the server.
-
-
Relying solely on server endpoints or basic browser storage (like localStorage) can quickly become unwieldy for larger or more complex use cases. Enter RxDB, a dedicated solution that manages data on the client side while offering real-time synchronization and offline-first capabilities.
RxDB (short for Reactive Database) is built on top of IndexedDB and leverages RxJS to provide a modern, reactive approach to handling data in the browser. With RxDB, you can store documents locally, query them in real-time, and synchronize changes with a remote server whenever an internet connection is available.
Reactive Data Handling: RxDB emits real-time updates whenever your data changes, allowing you to instantly reflect these changes in the DOM with jQuery.
-
Offline-First Approach: Keep your application usable even when the user's network is unavailable. Data is automatically synchronized once connectivity is restored.
-
Data Replication: Enable multi-device or multi-tab synchronization with minimal effort.
-
Observable Queries: Reduce code complexity by subscribing to queries instead of constantly polling for changes.
-
Multi-Tab Support: If a user opens your jQuery application in multiple tabs, RxDB keeps data in sync across all sessions.
-
-
3:45
This solved a problem I've had in Angular for years
RxDB is a client-side NoSQL database that stores data in the browser (or node.js) and synchronizes changes with other instances or servers. Its design embraces reactive programming principles, making it well-suited for real-time applications, offline scenarios, and multi-tab use cases.
RxDB's use of observables enables an event-driven architecture where data mutations automatically trigger UI updates. In a jQuery application, you can subscribe to these changes and update DOM elements as soon as data changes occur - no need for manual refresh or complicated change detection logic.
One of RxDB's distinguishing traits is its emphasis on offline-first design. This means your jQuery application continues to function, display, and update data even when there's no network connection. When connectivity is restored, RxDB synchronizes updates with the server or other peers, ensuring consistency across all instances.
RxDB supports real-time data replication with different backends. By enabling replication, you ensure that multiple clients - be they multiple browser tabs or separate devices - stay in sync. RxDB's conflict resolution strategies help keep the data consistent even when multiple users make changes simultaneously.
Instead of static queries, RxDB provides observable queries. Whenever data relevant to a query changes, RxDB re-emits the new result set. You can subscribe to these updates within your jQuery code and instantly reflect them in the UI.
Running your jQuery app in multiple tabs? RxDB automatically synchronizes changes between those tabs. Users can freely switch windows without missing real-time updates.
Historically, jQuery developers might use localStorage or raw IndexedDB for storing data. However, these solutions can require significant boilerplate, lack reactivity, and offer no built-in sync or conflict resolution. RxDB fills these gaps with an out-of-the-box solution, abstracting away low-level database complexities and providing an event-driven, offline-capable approach.
If your project isn't set up with a build process, you can still use bundlers like Webpack or Rollup, or serve RxDB as a UMD bundle. Once included, you'll have access to RxDB globally or via import statements.
Below is a minimal example of how to create an RxDB instance and collection. You can call this when your page initializes, then store the db object for later use:
Once you have your RxDB instance, you can query data reactively and use jQuery to manipulate the DOM:
-
// Example: Displaying heroes using jQuery
-$(document).ready(async function () {
- const db = await initDatabase();
-
- // Subscribing to all hero documents
- db.hero
- .find()
- .$ // the observable
- .subscribe((heroes) => {
- // Clear the list
- $('#heroList').empty();
-
- // Append each hero to the DOM
- heroes.forEach((hero) => {
- $('#heroList').append(`
- <li>
- <strong>${hero.name}</strong> - Points: ${hero.points}
- </li>
- `);
- });
- });
-
- // Example of adding a new hero
- $('#addHeroBtn').on('click', async () => {
- const heroName = $('#heroName').val();
- const heroPoints = parseInt($('#heroPoints').val(), 10);
- await db.hero.insert({
- id: Date.now().toString(),
- name: heroName,
- points: heroPoints
- });
- });
-});
-
With this approach, any time data in the hero collection changes - like when a new hero is added - your jQuery code re-renders the list of heroes automatically.
OPFS RxStorage: Uses the File System Access API for better performance in supported browsers.
-
Memory RxStorage: Stores data in memory, handy for tests or ephemeral data.
-
SQLite RxStorage: Uses SQLite (potentially via WebAssembly). In typical browser-based scenarios, localstorage or IndexedDB storage is usually more straightforward.
-
-
Synchronizing Data with RxDB between Clients and Servers
RxDB's offline-first approach allows your jQuery application to store and query data locally. Users can continue interacting, even offline. When connectivity returns, RxDB syncs to the server.
Should multiple clients update the same document, RxDB offers conflict handling strategies. You decide how to resolve conflicts - like keeping the latest edit or merging changes - ensuring data integrity across distributed systems.
With RxDB, data changes flow both ways: from client to server and from server to client. This real-time synchronization ensures that all users or tabs see consistent, up-to-date data.
Create indexes on frequently queried fields to speed up performance. For large data sets, indexing can drastically improve query times, keeping your jQuery UI snappy.
Use change streams to listen for data modifications at the database or collection level. This can trigger real-timeUI updates, notifications, or custom logic whenever the data changes.
If your data model has large or repetitive field names, JSON key compression can minimize stored document size and potentially boost performance.
-
Best Practices for Using RxDB in jQuery Applications
-
-
Centralize Your Database: Initialize and configure RxDB in one place. Expose the instance where needed or store it globally to avoid re-creating it on every script.
-
Leverage Observables: Instead of polling or manually refreshing data, rely on RxDB's reactivity. Subscribe to queries and let RxDB inform you when data changes.
-
Handle Subscriptions: If you create subscriptions in a single-page context, ensure you don't re-subscribe endlessly or create memory leaks. Clean them up if you're navigating away or removing DOM elements.
-
Offline Testing: Thoroughly test how your jQuery app behaves without a network connection. Simulate offline states in your browser's dev tools or with flight mode to ensure the user experience remains smooth.
-
Performance Profiling: For large data sets or frequent data updates, add indexes and carefully measure query performance. Optimize only where needed.
To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-
RxDB Examples: Browse official examples to see RxDB in action and learn best practices you can apply to your own project - even if jQuery isn't explicitly featured, the patterns are similar.
-
-
\ No newline at end of file
diff --git a/docs/articles/jquery-database.md b/docs/articles/jquery-database.md
deleted file mode 100644
index 604e55d4e9c..00000000000
--- a/docs/articles/jquery-database.md
+++ /dev/null
@@ -1,211 +0,0 @@
-# RxDB as a Database in a jQuery Application
-
-> Level up your jQuery-based projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.
-
-import {VideoBox} from '@site/src/components/video-box';
-
-# RxDB as a Database in a jQuery Application
-
-In the early days of dynamic web development, **jQuery** emerged as a popular library that simplified DOM manipulation and AJAX requests. Despite the rise of modern frameworks, many developers still maintain or extend existing jQuery projects, or leverage jQuery in specific contexts. As jQuery applications grow in complexity, they often require efficient data handling, offline support, and synchronization capabilities. This is where [RxDB](https://rxdb.info/), a reactive JavaScript database for the browser, node.js, and [mobile devices](./mobile-database.md), steps in.
-
-
-
-
-
-
-
-## jQuery Web Applications
-jQuery provides a simple API for DOM manipulation, event handling, and AJAX calls. It has been widely adopted due to its ease of use and strong community support. Many projects continue to rely on jQuery for handling client-side functionality, UI interactions, and animations. As these applications evolve, the need for a robust database solution that can manage data locally (and offline) becomes increasingly important.
-
-## Importance of Databases in jQuery Applications
-Modern, data-driven jQuery applications often need to:
-
-- **Store and retrieve data locally** for quick and responsive user experiences.
-- **Synchronize data** between clients or with a [central server](../rx-server.md).
-- **Handle offline scenarios** seamlessly.
-- **Handle large or complex data structures** without repeatedly hitting the server.
-
-Relying solely on server endpoints or basic browser storage (like `localStorage`) can quickly become unwieldy for larger or more complex use cases. Enter RxDB, a dedicated solution that manages data on the client side while offering real-time synchronization and offline-first capabilities.
-
-## Introducing RxDB as a Database Solution
-RxDB (short for Reactive Database) is built on top of [IndexedDB](./browser-database.md) and leverages [RxJS](https://rxjs.dev/) to provide a modern, reactive approach to handling data in the browser. With RxDB, you can store documents locally, query them in real-time, and synchronize changes with a remote server whenever an internet connection is available.
-
-### Key Features
-
-- **Reactive Data Handling**: RxDB emits real-time updates whenever your data changes, allowing you to instantly reflect these changes in the DOM with jQuery.
-- **Offline-First Approach**: Keep your application usable even when the user's network is unavailable. Data is automatically synchronized once connectivity is restored.
-- **Data Replication**: Enable multi-device or multi-tab synchronization with minimal effort.
-- **Observable Queries**: Reduce code complexity by subscribing to queries instead of constantly polling for changes.
-- **Multi-Tab Support**: If a user opens your jQuery application in multiple tabs, RxDB keeps data in sync across all sessions.
-
-
-
-
-
-## Getting Started with RxDB
-
-### What is RxDB?
-[RxDB](https://rxdb.info/) is a client-side NoSQL database that stores data in the browser (or [node.js](../nodejs-database.md)) and synchronizes changes with other instances or servers. Its design embraces reactive programming principles, making it well-suited for real-time applications, offline scenarios, and multi-tab use cases.
-
-
-
-### Reactive Data Handling
-RxDB's use of observables enables an event-driven architecture where data mutations automatically trigger UI updates. In a jQuery application, you can subscribe to these changes and update DOM elements as soon as data changes occur - no need for manual refresh or complicated change detection logic.
-
-### Offline-First Approach
-One of RxDB's distinguishing traits is its emphasis on offline-first design. This means your jQuery application continues to function, display, and update data even when there's no network connection. When connectivity is restored, RxDB synchronizes updates with the server or other peers, ensuring consistency across all instances.
-
-### Data Replication
-RxDB supports real-time data replication with different backends. By enabling replication, you ensure that multiple clients - be they multiple [browser](./browser-database.md) tabs or separate devices - stay in sync. RxDB's conflict resolution strategies help keep the data consistent even when multiple users make changes simultaneously.
-
-### Observable Queries
-Instead of static queries, RxDB provides observable queries. Whenever data relevant to a query changes, RxDB re-emits the new result set. You can subscribe to these updates within your jQuery code and instantly reflect them in the UI.
-
-### Multi-Tab Support
-Running your jQuery app in multiple tabs? RxDB automatically synchronizes changes between those tabs. Users can freely switch windows without missing real-time updates.
-
-
-
-### RxDB vs. Other jQuery Database Options
-Historically, jQuery developers might use `localStorage` or raw `IndexedDB` for storing data. However, these solutions can require significant boilerplate, lack reactivity, and offer no built-in sync or conflict resolution. RxDB fills these gaps with an out-of-the-box solution, abstracting away low-level database complexities and providing an event-driven, offline-capable approach.
-
-## Using RxDB in a jQuery Application
-
-### Installing RxDB
-Install RxDB (and `rxjs`) via npm or yarn:
-```bash
-npm install rxdb rxjs
-```
-
-If your project isn't set up with a build process, you can still use bundlers like Webpack or Rollup, or serve RxDB as a UMD bundle. Once included, you'll have access to RxDB globally or via import statements.
-
-## Creating and Configuring a Database
-
-Below is a minimal example of how to create an RxDB instance and collection. You can call this when your page initializes, then store the `db` object for later use:
-
-```js
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-async function initDatabase() {
- const db = await createRxDatabase({
- name: 'heroesdb',
- storage: getRxStorageLocalstorage(),
- password: 'myPassword', // optional encryption password
- multiInstance: true, // multi-tab support
- eventReduce: true // optimizes event handling
- });
-
- await db.addCollections({
- hero: {
- schema: {
- title: 'hero schema',
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: { type: 'string' },
- name: { type: 'string' },
- points: { type: 'number' }
- }
- }
- }
- });
-
- return db;
-}
-```
-
-## Updating the DOM with jQuery
-
-Once you have your RxDB instance, you can query data reactively and use jQuery to manipulate the DOM:
-
-```js
-// Example: Displaying heroes using jQuery
-$(document).ready(async function () {
- const db = await initDatabase();
-
- // Subscribing to all hero documents
- db.hero
- .find()
- .$ // the observable
- .subscribe((heroes) => {
- // Clear the list
- $('#heroList').empty();
-
- // Append each hero to the DOM
- heroes.forEach((hero) => {
- $('#heroList').append(`
-
- ${hero.name} - Points: ${hero.points}
-
- `);
- });
- });
-
- // Example of adding a new hero
- $('#addHeroBtn').on('click', async () => {
- const heroName = $('#heroName').val();
- const heroPoints = parseInt($('#heroPoints').val(), 10);
- await db.hero.insert({
- id: Date.now().toString(),
- name: heroName,
- points: heroPoints
- });
- });
-});
-```
-
-With this approach, any time data in the `hero` collection changes - like when a new hero is added - your jQuery code re-renders the list of heroes automatically.
-
-## Different RxStorage layers for RxDB
-
-RxDB supports multiple storage backends (RxStorage layers). Some popular ones:
-
-- [LocalStorage.js RxStorage](../rx-storage-localstorage.md): Uses the browsers [localstorage](./localstorage.md). Fast and easy to set up.
-- [IndexedDB RxStorage](../rx-storage-indexeddb.md): Direct IndexedDB usage, suitable for modern browsers.
-- [OPFS RxStorage](../rx-storage-opfs.md): Uses the File System Access API for better performance in supported browsers.
-- [Memory RxStorage](../rx-storage-memory.md): Stores data in memory, handy for tests or ephemeral data.
-- [SQLite RxStorage](../rx-storage-sqlite.md): Uses SQLite (potentially via WebAssembly). In typical browser-based scenarios, localstorage or IndexedDB storage is usually more straightforward.
-
-## Synchronizing Data with RxDB between Clients and Servers
-
-### Offline-First Approach
-RxDB's [offline-first](../offline-first.md) approach allows your jQuery application to store and query data locally. Users can continue interacting, even offline. When connectivity returns, RxDB syncs to the server.
-
-### Conflict Resolution
-Should multiple clients update the same document, RxDB offers [conflict handling strategies](../transactions-conflicts-revisions.md). You decide how to resolve conflicts - like keeping the latest edit or merging changes - ensuring data integrity across distributed systems.
-
-### Bidirectional Synchronization
-With RxDB, data changes flow both ways: from client to server and from server to client. This real-time synchronization ensures that all users or tabs see consistent, up-to-date data.
-
-
-
-## Advanced RxDB Features and Techniques
-
-### Indexing and Performance Optimization
-Create indexes on frequently queried fields to speed up performance. For large data sets, indexing can drastically improve query times, keeping your jQuery UI snappy.
-
-### Encryption of Local Data
-RxDB supports [encryption to secure data stored in the browser](../encryption.md). This is crucial if your application handles sensitive user information.
-
-### Change Streams and Event Handling
-Use change streams to listen for data modifications at the database or collection level. This can trigger [real-time](./realtime-database.md) [UI updates](./optimistic-ui.md), notifications, or custom logic whenever the data changes.
-
-### JSON Key Compression
-If your data model has large or repetitive field names, [JSON key compression](../key-compression.md) can minimize stored document size and potentially boost performance.
-
-## Best Practices for Using RxDB in jQuery Applications
-
-- Centralize Your Database: Initialize and configure RxDB in one place. Expose the instance where needed or store it globally to avoid re-creating it on every script.
-- Leverage Observables: Instead of polling or manually refreshing data, rely on RxDB's reactivity. Subscribe to queries and let RxDB inform you when data changes.
-- Handle Subscriptions: If you create subscriptions in a single-page context, ensure you don't re-subscribe endlessly or create memory leaks. Clean them up if you're navigating away or removing DOM elements.
-- Offline Testing: Thoroughly test how your jQuery app behaves without a network connection. Simulate offline states in your browser's dev tools or with flight mode to ensure the user experience remains smooth.
-- Performance Profiling: For large data sets or frequent data updates, add indexes and carefully measure query performance. Optimize only where needed.
-
-## Follow Up
-To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-- [RxDB GitHub Repository](/code/): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-- [RxDB Examples](https://github.com/pubkey/rxdb/tree/master/examples): Browse official examples to see RxDB in action and learn best practices you can apply to your own project - even if jQuery isn't explicitly featured, the patterns are similar.
diff --git a/docs/articles/json-based-database.html b/docs/articles/json-based-database.html
deleted file mode 100644
index 575b6c257b7..00000000000
--- a/docs/articles/json-based-database.html
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
-
-JSON-Based Databases - Why NoSQL and RxDB Simplify App Development | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
JSON-Based Databases: Why NoSQL and RxDB Simplify App Development
-
Modern applications handle highly dynamic, often deeply nested data structures—commonly represented in JSON. Whether you're building a real-time dashboard or a fully offline mobile app, storing and querying data in a JSON-friendly way can reduce overhead and coding complexity. This is where JSON-based databases (often part of the NoSQL family) come into play, letting you store objects in the same format they're used in your code, eliminating the schema wrangling that can come with a strict relational design.
-
Below, we explore why JSON-based databases naturally align with NoSQL principles, how relational engines (like PostgreSQL or SQLite) handle JSON columns, the pitfalls of storing data in a single plain JSON text file, and the ways RxDB stands out as an offline-first JSON solution for JavaScript developers—complete with advanced features like JSON-Schema and JSON-key-compression.
When your data is stored as JSON, each record or document can hold nested arrays and sub-objects with no forced table schema. NoSQL solutions such as MongoDB, CouchDB, Firebase, and RxDB store and retrieve these documents in their “raw” JSON form. This model integrates smoothly with how front-end applications already handle data, minimizing transformations and improving developer productivity.
Traditional SQL tables enforce rigid column definitions and demand explicit schema migrations when you add or rename a field. By contrast, NoSQL solutions accept more dynamic data structures, allowing changes on the fly. This means a front-end developer can add a new field to a JSON object—perhaps for a new feature—without the friction of redefining or migrating a database schema. While this is possible, it is often not recommended.
As modern UIs frequently manipulate deeply nested or changing data, developers find it easier to store whole objects directly, saving time that might otherwise be spent performing complex joins or normalizing data. For instance, frameworks like React, Vue, or Angular are inherently comfortable with nested JSON structures, which map more directly to NoSQL’s “document” approach than to relational tables.
It depends on your application. SQL remains exceptional for complex aggregations, enforced relationships, and sophisticated transaction handling. But NoSQL is often more intuitive and easier to maintain for “document-first” applications that:
-
-
Thrive on flexible or rapidly evolving data models.
-
Rely on hierarchical or nested JSON objects.
-
Avoid multi-table joins.
-
Require easy horizontal scaling for large sets of documents.
-
-
Relational databases are still a top choice for many enterprise back-ends, especially when advanced analytics or strongly enforced referential integrity is needed. But if your application is predominantly storing and manipulating JSON documents (e.g., user profiles, real-time chat logs, embedded items), a JSON-based or document-oriented approach can greatly reduce friction during development.
NoSQL solutions—particularly JSON-based document stores—provide a natural fit for flexible, nested data in UI-heavy applications. However, certain scenarios may benefit more from a SQL solution:
-
-
Complex Relationships: If your data demands intricate joins across multiple entities (e.g., many-to-many relationships that can’t easily be embedded in a single document), a well-structured relational schema can simplify queries.
-
Strong Integrity and Constraints: SQL excels at enforcing constraints such as foreign keys, unique constraints, and advanced triggers. If your system needs strict data validation and complex business logic within the database, SQL might prove more robust.
-
High-End Analytical Queries: Relational databases can handle sophisticated aggregations, groupings, and joins more efficiently. If your app frequently runs advanced SQL queries, a NoSQL approach may complicate or slow down analytics.
-
Legacy Integration: Many enterprise systems are built around existing relational schemas. A purely NoSQL approach might mean rewriting or bridging systems that are heavily reliant on SQL constraints and transformations.
-
Transaction Handling: While many NoSQL solutions have improved transaction support, it can still lag behind well-established SQL transaction models. If ACID properties and multi-operation atomicity are paramount, you might prefer a tried-and-true relational engine.
-
-
In short, if you prioritize advanced relational queries, robust constraints, or complex business rules at the database level, SQL remains a powerful, and possibly superior, choice. For user-centric, fast-evolving JSON data, though, NoSQL or JSON-based solutions often reduce the friction of frequent schema changes.
To accommodate the demand for flexible data, several SQL engines (notably PostgreSQL and MySQL) introduced support for JSON columns. PostgreSQL offers the JSON and JSONB types, enabling developers to store raw JSON in a column. You can also index specific paths within the JSON to speed lookups on nested fields:
-
CREATE TABLE products (
- id SERIAL PRIMARY KEY,
- name TEXT,
- details JSONB
-);
-
--- Insert a record with JSON data
-INSERT INTO products (name, details) VALUES
-('Laptop', '{"brand": "BrandX", "features": ["Touchscreen", "SSD"]}');
-
Although this approach merges the best of both worlds (SQL queries + flexible JSON fields), it can also create a “split personality” in your schema. You might store stable data in normal columns, while unpredictable or nested details live inside a JSONB field. Some projects flourish with this hybrid design, others find it a bit unwieldy.
SQLite also allows storing JSON data, typically as text columns, but with some additional features since SQLite 3.9 (2015) including the JSON1 extension. This extension can parse JSON text, perform queries on JSON fields, and do partial updates. However, storing JSON in SQLite does require you to ensure you’ve compiled SQLite with JSON1 support or to rely on a library that bundles it. While possible, you still won't get quite the same schema-agnostic ease as a full document store, but it’s a pragmatic solution for smaller or embedded needs on the server side—or occasionally in the browser if you run SQLite via WebAssembly. RxDB uses this in its SQLite storage.
-
JSON vs. Database - Why a Plain JSON Text File is a Problem
-
Some developers consider storing everything in a single JSON file, typically read and written directly from disk or local storage. This approach, while seemingly simple, usually does not scale. Key issues include:
-
-
No Concurrency: If multiple parts of the application try to write to the same JSON file, you risk overwriting changes.
-
No Indexes: Finding or filtering items in large JSON text requires scanning everything. This is slow and quickly becomes unmanageable.
-
No Partial Updates: You often reload the entire file, modify it in memory, then write it back, which is highly inefficient for large data sets.
-
Corruption Risk: A single corrupted write or partial save might break the entire JSON file, losing all data.
-
High Memory Usage: The entire file may need to be parsed into memory, even if you only need a fraction of the data.
-
-
Databases—relational or NoSQL—solve these issues by handling concurrency, enabling partial reads/writes, establishing indexes, and ensuring transactional integrity so you don’t lose everything if the process is interrupted mid-write.
-
-
RxDB: A JSON-Focused Database for JavaScript Apps
-
Many NoSQL databases operate on the server, whereas RxDB is built for client-side usage—browsers, mobile apps, or Node.js. It specializes in JSON documents and embraces an offline-first philosophy.
RxDB stores each record as a JSON document, closely matching how front-end frameworks handle state. This eliminates complex transformations or manual JSON parsing before writing to a table.
-
-
Reactive Queries
-
-
Instead of complex SQL, RxDB uses JSON-based query definitions. You can subscribe to query results, letting your UI automatically refresh when data changes locally or from remote sync updates:
-
-
-
Offline-First Sync
-
-
Built-in replication plugins push/pull changes to or from a remote server. If your app is offline, updates get stored locally, then sync up seamlessly once a connection is available.
-
-
Optional JSON-Schema
-
-
Though it’s a document database, RxDB encourages you to define a JSON-based schema for clarity, indexing, and type validation. This helps maintain data consistency while still allowing a measure of flexibility for new fields.
JSON-Schema: By specifying a JSON-Schema, you can define which fields exist, whether they are required, and their data types. This is invaluable for catching malformed documents early and imposing mild structure in a NoSQL setting.
-
-
-
JSON Key-Compression: Large, verbose field names can bloat storage usage. RxDB’s optional key-compression plugin automatically shortens field names in your JSON documents internally, reducing disk space and bandwidth:
JSON-based databases naturally align with NoSQL because they accommodate evolving, nested data without rigid schemas. This makes them appealing for many UI-centric or offline-first applications where flexible documents and agile development cycles matter more than heavy relational queries or constraints.
-
SQL can still store JSON—whether in PostgreSQL’s JSONB columns, MySQL’s JSON fields, or SQLite’s JSON1 extension. For some teams, a hybrid approach pairing SQL for relational data with JSON columns for more flexible fields works well. However, storing everything in a single monolithic JSON text file is rarely advisable for anything beyond trivial tasks—databases excel at concurrency, indexing, and partial writes.
-
Tools like RxDB provide an even simpler, local-first take on JSON documents—particularly for JavaScript projects. With offline replication, reactive queries, optional JSON-Schema, and advanced optimizations such as key-compression, RxDB streamlines building dynamic, user-facing features while preserving the core benefits of a robust document database.
-
To explore more about RxDB and its capabilities for browser database development, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-
RxDB Examples: Browse official examples to see RxDB in action and learn best practices you can apply to your own project - even if jQuery isn't explicitly featured, the patterns are similar.
-
-
\ No newline at end of file
diff --git a/docs/articles/json-based-database.md b/docs/articles/json-based-database.md
deleted file mode 100644
index ee601b6af8c..00000000000
--- a/docs/articles/json-based-database.md
+++ /dev/null
@@ -1,157 +0,0 @@
-# JSON-Based Databases - Why NoSQL and RxDB Simplify App Development
-
-> Dive into how JSON-based databases power modern UI-centric apps, why NoSQL often outperforms SQL for dynamic data, how SQLite accommodates JSON, and why RxDB delivers a seamless offline-first solution for JavaScript with advanced JSON features.
-
-# JSON-Based Databases: Why NoSQL and RxDB Simplify App Development
-
-Modern applications handle highly dynamic, often deeply nested data structures—commonly represented in **JSON**. Whether you're building a real-time dashboard or a fully offline mobile app, storing and querying data in a JSON-friendly way can reduce overhead and coding complexity. This is where **JSON-based databases** (often part of the **NoSQL** family) come into play, letting you store objects in the same format they're used in your code, eliminating the schema wrangling that can come with a strict relational design.
-
-Below, we explore why JSON-based databases naturally align with **NoSQL** principles, how relational engines (like PostgreSQL or SQLite) handle JSON columns, the pitfalls of storing data in a single plain JSON text file, and the ways [RxDB](https://rxdb.info/) stands out as an offline-first JSON solution for JavaScript developers—complete with advanced features like JSON-Schema and JSON-key-compression.
-
-
-
-## Why JSON-Based Databases Are Typically NoSQL
-
-### Document-Oriented by Nature
-
-When your data is stored as JSON, each record or document can hold nested arrays and sub-objects with no forced table schema. NoSQL solutions such as [MongoDB](../rx-storage-mongodb.md), [CouchDB](../replication-couchdb.md), [Firebase](../replication-firestore.md), and **RxDB** store and retrieve these documents in their “raw” JSON form. This model integrates smoothly with how front-end applications already handle data, minimizing transformations and improving developer productivity.
-
-### Flexible, Schema-Agnostic
-
-Traditional SQL tables enforce rigid column definitions and demand explicit schema migrations when you add or rename a field. By contrast, NoSQL solutions accept more dynamic data structures, allowing changes on the fly. This means a front-end developer can add a new field to a JSON object—perhaps for a new feature—without the friction of redefining or migrating a database schema. While this is possible, it is often not recommended.
-
-### Aligned With Evolving User Interfaces
-
-As modern UIs frequently manipulate deeply nested or changing data, developers find it easier to store whole objects directly, saving time that might otherwise be spent performing complex joins or normalizing data. For instance, frameworks like React, Vue, or Angular are inherently comfortable with nested JSON structures, which map more directly to NoSQL’s “document” approach than to relational tables.
-
-## Is NoSQL “Better” Than SQL?
-
-It depends on your application. **SQL** remains exceptional for complex aggregations, enforced relationships, and sophisticated transaction handling. But **NoSQL** is often more intuitive and easier to maintain for “document-first” applications that:
-
-- Thrive on flexible or rapidly evolving data models.
-- Rely on hierarchical or nested JSON objects.
-- Avoid multi-table joins.
-- Require easy horizontal scaling for large sets of documents.
-
-Relational databases are still a top choice for many enterprise back-ends, especially when advanced analytics or strongly enforced referential integrity is needed. But if your application is predominantly storing and manipulating JSON documents (e.g., user profiles, real-time chat logs, embedded items), a JSON-based or document-oriented approach can greatly reduce friction during development.
-
-## When to Prefer SQL Instead of JSON/NoSQL
-NoSQL solutions—particularly JSON-based document stores—provide a natural fit for flexible, nested data in UI-heavy applications. However, certain scenarios may benefit more from a **SQL** solution:
-1. **Complex Relationships**: If your data demands intricate joins across multiple entities (e.g., many-to-many relationships that can’t easily be embedded in a single document), a well-structured relational schema can simplify queries.
-2. **Strong Integrity and Constraints**: SQL excels at enforcing constraints such as foreign keys, unique constraints, and advanced triggers. If your system needs strict data validation and complex business logic within the database, SQL might prove more robust.
-3. **High-End Analytical Queries**: Relational databases can handle sophisticated aggregations, groupings, and joins more efficiently. If your app frequently runs advanced SQL queries, a NoSQL approach may complicate or slow down analytics.
-4. **Legacy Integration**: Many enterprise systems are built around existing relational schemas. A purely NoSQL approach might mean rewriting or bridging systems that are heavily reliant on SQL constraints and transformations.
-5. **Transaction Handling**: While many NoSQL solutions have improved transaction support, it can still lag behind well-established SQL transaction models. If ACID properties and multi-operation atomicity are paramount, you might prefer a tried-and-true relational engine.
-
-In short, if you prioritize advanced relational queries, robust constraints, or complex business rules at the database level, SQL remains a powerful, and possibly superior, choice. For user-centric, fast-evolving JSON data, though, NoSQL or JSON-based solutions often reduce the friction of frequent schema changes.
-
-## Storing JSON in Traditional SQL Databases
-
-### JSON Columns in PostgreSQL or MySQL
-
-To accommodate the demand for flexible data, several SQL engines (notably **PostgreSQL** and MySQL) introduced support for **JSON** columns. PostgreSQL offers the `JSON` and `JSONB` types, enabling developers to store raw JSON in a column. You can also index specific paths within the JSON to speed lookups on nested fields:
-
-```sql
-CREATE TABLE products (
- id SERIAL PRIMARY KEY,
- name TEXT,
- details JSONB
-);
-
--- Insert a record with JSON data
-INSERT INTO products (name, details) VALUES
-('Laptop', '{"brand": "BrandX", "features": ["Touchscreen", "SSD"]}');
-```
-
-Although this approach merges the best of both worlds (SQL queries + flexible JSON fields), it can also create a “split personality” in your schema. You might store stable data in normal columns, while unpredictable or nested details live inside a JSONB field. Some projects flourish with this hybrid design, others find it a bit unwieldy.
-
-
-
-
-
-## Storing JSON in SQLite
-SQLite also allows storing JSON data, typically as text columns, but with some additional features since **SQLite 3.9** (2015) including the [JSON1 extension](https://www.sqlite.org/json1.html). This extension can parse JSON text, perform queries on JSON fields, and do partial updates. However, storing JSON in SQLite does require you to ensure you’ve compiled SQLite with JSON1 support or to rely on a library that bundles it. While possible, you still won't get quite the same schema-agnostic ease as a full document store, but it’s a pragmatic solution for smaller or embedded needs on the server side—or occasionally in the browser if you run SQLite via WebAssembly. RxDB uses this in its [SQLite storage](../rx-storage-sqlite.md).
-
-## JSON vs. Database - Why a Plain JSON Text File is a Problem
-
-Some developers consider storing everything in a single JSON file, typically read and written directly from disk or local storage. This approach, while seemingly simple, usually does not scale. Key issues include:
-
-- **No Concurrency**: If multiple parts of the application try to write to the same JSON file, you risk overwriting changes.
-- **No Indexes**: Finding or filtering items in large JSON text requires scanning everything. This is slow and quickly becomes unmanageable.
-- **No Partial Updates**: You often reload the entire file, modify it in memory, then write it back, which is highly inefficient for large data sets.
-- **Corruption Risk**: A single corrupted write or partial save might break the entire JSON file, losing all data.
-- **High Memory Usage**: The entire file may need to be parsed into memory, even if you only need a fraction of the data.
-
-Databases—relational or NoSQL—solve these issues by handling concurrency, enabling partial reads/writes, establishing indexes, and ensuring transactional integrity so you don’t lose everything if the process is interrupted mid-write.
-
-
-
-
-
-
-
-## RxDB: A JSON-Focused Database for JavaScript Apps
-
-Many NoSQL databases operate on the server, whereas RxDB is built for client-side usage—browsers, mobile apps, or Node.js. It specializes in JSON documents and embraces an [offline-first](../offline-first.md) philosophy.
-
-### Key Characteristics
-1. **Local JSON Storage**
-
-RxDB stores each record as a JSON document, closely matching how front-end frameworks handle state. This eliminates complex transformations or manual JSON parsing before writing to a table.
-
-2. **Reactive Queries**
-
-Instead of complex SQL, RxDB uses JSON-based [query](../rx-query.md) definitions. You can subscribe to query results, letting your UI automatically refresh when data changes locally or from remote sync updates:
-
-
-
-3. **Offline-First Sync**
-
-Built-in replication plugins push/pull changes to or from a remote server. If your app is offline, updates get stored locally, then sync up seamlessly once a connection is available.
-
-4. **Optional JSON-Schema**
-
-Though it’s a document database, RxDB encourages you to define a JSON-based schema for clarity, indexing, and type validation. This helps maintain data consistency while still allowing a measure of flexibility for new fields.
-
-### Advanced JSON Features in RxDB
-
-- **JSON-Schema**: By specifying a JSON-Schema, you can define which fields exist, whether they are required, and their data types. This is invaluable for catching malformed documents early and imposing mild structure in a NoSQL setting.
-
-- **JSON Key-Compression**: Large, verbose field names can bloat storage usage. RxDB’s optional [key-compression plugin](../key-compression.md) automatically shortens field names in your JSON documents internally, reducing disk space and bandwidth:
-
-```ts
-// Example: how key-compression can transform your documents
-const uncompressed = {
- "firstName": "Corrine",
- "lastName": "Ziemann",
- "shoppingCartItems": [
- { "productNumber": 29857, "amount": 1 },
- { "productNumber": 53409, "amount": 6 }
- ]
-};
-
-const compressed = {
- "|e": "Corrine",
- "|g": "Ziemann",
- "|i": [
- { "|h": 29857, "|b": 1 },
- { "|h": 53409, "|b": 6 }
- ]
-};
-```
-
-The user sees no difference in their code—RxDB automatically decompresses data on read—but the overhead is drastically reduced behind the scenes.
-
-## Follow Up
-
-JSON-based databases naturally align with NoSQL because they accommodate evolving, nested data without rigid schemas. This makes them appealing for many UI-centric or offline-first applications where flexible documents and agile development cycles matter more than heavy relational queries or constraints.
-
-SQL can still store JSON—whether in PostgreSQL’s JSONB columns, MySQL’s JSON fields, or SQLite’s JSON1 extension. For some teams, a hybrid approach pairing SQL for relational data with JSON columns for more flexible fields works well. However, storing everything in a single monolithic JSON text file is rarely advisable for anything beyond trivial tasks—databases excel at concurrency, indexing, and partial writes.
-
-Tools like RxDB provide an even simpler, [local-first](./local-first-future.md) take on JSON documents—particularly for JavaScript projects. With offline [replication](../replication.md), reactive queries, optional JSON-Schema, and advanced optimizations such as key-compression, RxDB streamlines building dynamic, user-facing features while preserving the core benefits of a robust document database.
-
-To explore more about RxDB and its capabilities for browser database development, check out the following resources:
-
-- [RxDB GitHub Repository](/code/): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-- [RxDB Examples](https://github.com/pubkey/rxdb/tree/master/examples): Browse official examples to see RxDB in action and learn best practices you can apply to your own project - even if jQuery isn't explicitly featured, the patterns are similar.
diff --git a/docs/articles/json-database.html b/docs/articles/json-database.html
deleted file mode 100644
index 14a441fc457..00000000000
--- a/docs/articles/json-database.html
+++ /dev/null
@@ -1,140 +0,0 @@
-
-
-
-
-
-RxDB - The JSON Database Built for JavaScript | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Storing data as JSON documents in a NoSQL database is not just a trend; it's a practical choice. JSON data is highly compatible with various tools and is human-readable, making it an excellent fit for modern applications. JSON documents offer more flexibility compared to traditional SQL table rows, as they can contain nested data structures. This article introduces RxDB, an open-source, flexible, performant, and battle-tested NoSQL JSON database specifically designed for JavaScript applications.
JavaScript Friendliness: JavaScript, a prevalent language for web development, naturally uses JSON for data representation. Using a JSON database aligns seamlessly with JavaScript's native data format.
-
-
-
Compatibility: JSON is widely supported across different programming languages and platforms. Storing data in JSON format ensures compatibility with a broad range of tools and systems. All modern programming ecosystems have packages to parse, validate and process JSON data.
-
-
-
Flexibility: JSON documents can accommodate complex and nested data structures, allowing developers to store data in a more intuitive and hierarchical manner compared to SQL table rows. Nested data can be just stored in-document instead of having related tables.
-
-
-
Human-Readable: JSON is easy to read and understand, simplifying debugging and data inspection tasks.
When incorporating JSON documents into your application, you have several storage and access options to consider:
-
-
-
Local In-App Database with In-Memory Storage: Ideal for lightweight applications or temporary data storage, this option keeps data in memory, ensuring fast read and write operations. However, data is not persistet beyond the current application session, making it suitable for temporary data storage. With RxDB, the memory RxStorage can be utilized to create an in-memory database.
-
-
-
Local In-App Database with Persistent Storage: Suitable for applications requiring data retention across sessions. Data is stored on the user's device or inside of the Node.js application, offering persistence between application sessions. It balances speed and data retention, making it versatile for various applications. With RxDB, a whole range of persistent storages is available. As example, for browser there is the IndexedDB storage. For server side applications, the Node.js Filesystem storage can be used. There are many more storages for React-Native, Flutter, Capacitors.js and others.
-
-
-
Server Database Connected to the Application: For applications requiring data synchronization and accessibility from multiple processes, a server-based database is the preferred choice. Data is stored on a remote server, facilitating data sharing, synchronization, and accessibility across multiple processes. It's suitable for scenarios requiring centralized data management and enhanced security and backup capabilities on the server. RxDB supports the FoundationDB and MongoDB as a remote database server.
Compression storage for JSON documents is made effortless with RxDB's key-compression plugin. This feature enables the efficient storage of compressed document data, reducing storage requirements while maintaining data integrity. Queries on compressed documents remain seamless, ensuring that your application benefits from both space-saving advantages and optimal query performance, making RxDB a compelling choice for managing JSON data efficiently. The compression happens inside of the RxDatabase and does not affect the API usage. The only limitation is that encrypted fields themself cannot be used inside a query.
-
Schema Validation and Data Migration on Schema Changes
-
Storing JSON documents inside of a database in an application, can cause a problem when the format of the data changes. Instead of having a single server where the data must be migrated, many client devices are out there that have to run a migration.
-When your application's schema evolves, RxDB provides migration strategies to facilitate the transition, ensuring data consistency throughout schema updates.
-
JSONSchema Validation Plugins: RxDB supports multiple JSONSchema validation plugins, guaranteeing that only valid data is stored in the database. RxDB uses the JsonSchema standardization that you might know from other technologies like OpenAPI (aka Swagger).
RxDB offers versatile storage solutions for browser-based applications:
-
-
-
Multiple Storage Plugins: RxDB supports various storage backends, including IndexedDB, localstorage and In-Memory, catering to a range of browser environments.
-
-
-
Observable Queries: With RxDB, you can create observable queries that work seamlessly across multiple browser tabs, providing real-time updates and synchronization.
Certainly! Let's delve deeper into the performance aspects of RxDB when it comes to working with JSON data.
-
-
-
Efficient Querying: RxDB is engineered for rapid and efficient querying of JSON data. It employs a well-optimized indexing system that allows for lightning-fast retrieval of specific data points within your JSON documents. Whether you're fetching individual values or complex nested structures, RxDB's query performance is designed to keep your application responsive, even when dealing with large datasets.
-
-
-
Scalability: As your application grows and your JSON dataset expands, RxDB scales gracefully. Its performance remains consistent, enabling you to handle increasingly larger volumes of data without compromising on speed or responsiveness. This scalability is essential for applications that need to accommodate growing user bases and evolving data needs.
-
-
-
Reduced Latency: RxDB's streamlined data access mechanisms significantly reduce latency when working with JSON data. Whether you're reading from the database, making updates, or synchronizing data between clients and servers, RxDB's optimized operations help minimize the delays often associated with data access. Observed queries are optimized with the EventReduce algorithm to provide nearly-instand UI updates on data changes.
-
-
-
RxStorage Layer: Because RxDB allows you to swap out the storage layer. A storage with the most optimal performance can be chosen for each runtime while not touching other database code. Depending on the access patterns, you can pick exactly the storage that is best:
Node.js developers can also benefit from RxDB's capabilities. By integrating RxDB into your Node.js applications, you can harness the power of a NoSQL JSON db to efficiently manage your data on the server-side. RxDB's flexibility, performance, and essential features are equally valuable in server-side development. Read more about RxDB+Node.js.
For mobile app developers working with React Native, RxDB offers a convenient solution for handling JSON data. Whether you're building Android or iOS applications, RxDB's compatibility with JavaScript and its ability to work with JSON documents make it a natural choice for data management within your React Native apps. Read more about RxDB+React-Native.
In some cases, you might want to use SQLite as a backend storage solution for your JSON data. RxDB can be configured to work with SQLite, providing the benefits of both a relational database system and JSON document storage. This hybrid approach can be advantageous when dealing with complex data relationships while retaining the flexibility of JSON data representation.
To further explore RxDB and get started with using it in your frontend applications, consider the following resources:
-
-
RxDB Quickstart: A step-by-step guide to quickly set up RxDB in your project and start leveraging its features.
-
RxDB GitHub Repository: The official repository for RxDB, where you can find the code, examples, and community support.
-
-
By embracing RxDB as your JSON database solution, you can tap into the extensive capabilities of JSON data storage. This empowers your applications with offline accessibility, caching, enhanced performance, and effortless data synchronization. RxDB's focus on JavaScript and its robust feature set render it the perfect selection for frontend developers in pursuit of efficient and scalable data storage solutions.
-
-
\ No newline at end of file
diff --git a/docs/articles/json-database.md b/docs/articles/json-database.md
deleted file mode 100644
index b6ab14d1b8b..00000000000
--- a/docs/articles/json-database.md
+++ /dev/null
@@ -1,109 +0,0 @@
-# RxDB - The JSON Database Built for JavaScript
-
-> Experience a powerful JSON database with RxDB, built for JavaScript. Store, sync, and compress your data seamlessly across web and mobile apps.
-
-# RxDB - JSON Database for JavaScript
-
-Storing data as **JSON documents** in a **NoSQL** database is not just a trend; it's a practical choice. JSON data is highly compatible with various tools and is human-readable, making it an excellent fit for modern applications. JSON documents offer more flexibility compared to traditional SQL table rows, as they can contain nested data structures. This article introduces [RxDB](https://rxdb.info/), an open-source, flexible, performant, and battle-tested NoSQL JSON database specifically designed for **JavaScript** applications.
-
-
-
-
-
-
-
-## Why Choose a JSON Database?
-- **JavaScript Friendliness**: JavaScript, a prevalent language for web development, naturally uses JSON for data representation. Using a JSON database aligns seamlessly with JavaScript's native data format.
-
-- **Compatibility**: JSON is widely supported across different programming languages and platforms. Storing data in JSON format ensures compatibility with a broad range of tools and systems. All modern programming ecosystems have packages to parse, validate and process JSON data.
-
-- **Flexibility**: JSON documents can accommodate complex and nested data structures, allowing developers to store data in a more intuitive and hierarchical manner compared to SQL table rows. Nested data can be just stored in-document instead of having related tables.
-
-- **Human-Readable**: JSON is easy to read and understand, simplifying debugging and data inspection tasks.
-
-## Storage and Access Options for JSON Documents
-When incorporating JSON documents into your application, you have several storage and access options to consider:
-
-- **Local In-App Database with In-Memory Storage**: Ideal for lightweight applications or temporary data storage, this option keeps data in memory, ensuring fast read and write operations. However, data is not persistet beyond the current application session, making it suitable for temporary data storage. With RxDB, the [memory RxStorage](../rx-storage-memory.md) can be utilized to create an in-memory database.
-
-- **Local In-App Database with Persistent Storage**: Suitable for applications requiring data retention across sessions. Data is stored on the user's device or inside of the Node.js application, offering persistence between application sessions. It balances speed and data retention, making it versatile for various applications. With RxDB, a whole range of persistent storages is available. As example, for browser there is the [IndexedDB storage](../rx-storage-indexeddb.md). For server side applications, the [Node.js Filesystem storage](../rx-storage-filesystem-node.md) can be used. There are [many more storages](../rx-storage.md) for React-Native, Flutter, Capacitors.js and others.
-
-- **Server Database Connected to the Application**: For applications requiring data synchronization and accessibility from multiple processes, a server-based database is the preferred choice. Data is stored on a **remote server**, facilitating data sharing, synchronization, and accessibility across multiple processes. It's suitable for scenarios requiring centralized data management and enhanced security and backup capabilities on the server. RxDB supports the [FoundationDB](../rx-storage-foundationdb.md) and [MongoDB](../rx-storage-mongodb.md) as a remote database server.
-
-## Compression Storage for JSON Documents
-
-Compression storage for JSON documents is made effortless with RxDB's [key-compression plugin](../key-compression.md). This feature enables the efficient storage of compressed document data, reducing storage requirements while maintaining data integrity. Queries on compressed documents remain seamless, ensuring that your application benefits from both space-saving advantages and optimal query performance, making RxDB a compelling choice for managing JSON data efficiently. The compression happens inside of the [RxDatabase](../rx-database.md) and does not affect the API usage. The only limitation is that encrypted fields themself cannot be used inside a query.
-
-## Schema Validation and Data Migration on Schema Changes
-Storing JSON documents inside of a database in an application, can cause a problem when the format of the data changes. Instead of having a single server where the data must be migrated, many client devices are out there that have to run a migration.
-When your application's schema evolves, RxDB provides [migration strategies](../migration-schema.md) to facilitate the transition, ensuring data consistency throughout schema updates.
-
-**JSONSchema Validation Plugins**: RxDB supports multiple [JSONSchema validation plugins](../schema-validation.md), guaranteeing that only valid data is stored in the database. RxDB uses the JsonSchema standardization that you might know from other technologies like OpenAPI (aka Swagger).
-
-```javascript
-// RxDB Schema example
-const mySchema = {
- version: 0,
- primaryKey: 'id', // <- define the primary key for your documents
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100 // <- the primary key must have set maxLength
- },
- name: {
- type: 'string',
- maxLength: 100
- },
- done: {
- type: 'boolean'
- },
- timestamp: {
- type: 'string',
- format: 'date-time'
- }
- },
- required: ['id', 'name', 'done', 'timestamp']
-}
-```
-
-## Store JSON with RxDB in Browser Applications
-RxDB offers versatile storage solutions for browser-based applications:
-
-- **Multiple Storage Plugins**: RxDB supports various storage backends, including [IndexedDB](../rx-storage-indexeddb.md), [localstorage](../rx-storage-localstorage.md) and [In-Memory](../rx-storage-memory.md), catering to a range of browser environments.
-
-- **Observable Queries**: With RxDB, you can create observable [queries](../rx-query.md) that work seamlessly across multiple browser tabs, providing real-time updates and synchronization.
-
-
-
-## RxDB JSON Database Performance
-
-Certainly! Let's delve deeper into the performance aspects of RxDB when it comes to working with JSON data.
-
-1. **Efficient Querying:** RxDB is engineered for rapid and efficient querying of JSON data. It employs a well-optimized indexing system that allows for lightning-fast retrieval of specific data points within your JSON documents. Whether you're fetching individual values or complex nested structures, RxDB's query performance is designed to keep your application responsive, even when dealing with large datasets.
-
-2. **Scalability:** As your application grows and your [JSON dataset](./json-based-database.md) expands, RxDB scales gracefully. Its performance remains consistent, enabling you to handle increasingly larger volumes of data without compromising on speed or responsiveness. This scalability is essential for applications that need to accommodate growing user bases and evolving data needs.
-
-3. **Reduced Latency:** RxDB's streamlined data access mechanisms significantly reduce latency when working with JSON data. Whether you're reading from the database, making updates, or synchronizing data between clients and servers, RxDB's optimized operations help minimize the delays often associated with data access. Observed queries are optimized with the [EventReduce algorithm](https://github.com/pubkey/event-reduce) to provide nearly-instand UI updates on data changes.
-
-4. **RxStorage Layer**: Because RxDB allows you to swap out the storage layer. A storage with the most optimal performance can be chosen for each runtime while not touching other database code. Depending on the access patterns, you can pick exactly the storage that is best:
-
-
-
-## RxDB in Node.js
-Node.js developers can also benefit from RxDB's capabilities. By integrating RxDB into your Node.js applications, you can harness the power of a NoSQL JSON db to efficiently manage your data on the server-side. RxDB's flexibility, performance, and essential features are equally valuable in server-side development. [Read more about RxDB+Node.js](../nodejs-database.md).
-
-## RxDB to store JSON documents in React Native
-
-For mobile app developers working with React Native, RxDB offers a convenient solution for handling JSON data. Whether you're building Android or iOS applications, RxDB's compatibility with JavaScript and its ability to work with JSON documents make it a natural choice for data management within your React Native apps. [Read more about RxDB+React-Native](../react-native-database.md).
-
-## Using SQLite as a JSON Database
-In some cases, you might want to use SQLite as a backend storage solution for your JSON data. RxDB can be configured [to work with SQLite](../rx-storage-sqlite.md), providing the benefits of both a relational database system and JSON document storage. This hybrid approach can be advantageous when dealing with complex data relationships while retaining the flexibility of JSON data representation.
-
-## Follow Up
-To further explore RxDB and get started with using it in your frontend applications, consider the following resources:
-
-- [RxDB Quickstart](../quickstart.md): A step-by-step guide to quickly set up RxDB in your project and start leveraging its features.
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): The official repository for RxDB, where you can find the code, examples, and community support.
-
-By embracing [RxDB](https://rxdb.info/) as your **JSON database** solution, you can tap into the extensive capabilities of JSON data storage. This empowers your applications with offline accessibility, caching, enhanced performance, and effortless data synchronization. RxDB's focus on JavaScript and its robust feature set render it the perfect selection for frontend developers in pursuit of efficient and scalable data storage solutions.
diff --git a/docs/articles/local-database.html b/docs/articles/local-database.html
deleted file mode 100644
index de9a616761f..00000000000
--- a/docs/articles/local-database.html
+++ /dev/null
@@ -1,116 +0,0 @@
-
-
-
-
-
-What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications
-
A local database is a data storage system residing on a user's device, allowing applications to store, query, and manipulate information without needing continuous network access. This approach prioritizes quick data retrieval, efficient updates, and the ability to function in offline-first scenarios. In contrast, server-based databases require an active internet connection for each request and response cycle, making them more vulnerable to latency, network disruptions, and downtime.
-
Local databases often leverage technologies such as IndexedDB, SQLite, or WebSQL (though WebSQL has been deprecated). These technologies manage both structured data (like relational tables) and unstructured data (such as JSON documents). When connectivity is restored, local databases typically sync their changes back to a central server-side database, maintaining consistent and up-to-date records across multiple devices.
Offline Functionality: Essential for apps that must remain usable without a consistent internet connection, such as note-taking apps or offline-first CRMs. Users can continue adding and editing data, then sync changes once they reconnect.
-
Low Latency: By reducing round-trips to remote servers, local databases enable real-time responsiveness. This feature is critical for interactive applications such as gaming platforms, data dashboards, or analytics tools that need near-instant feedback.
-
Data Synchronization: Many modern applications - like chat systems or collaborative editing tools - require continuous data exchange between multiple users or devices. Local databases can handle intermittent connectivity gracefully, queuing updates locally and syncing them when possible.
-
-
In addition, local databases are increasingly integral to Progressive Web Apps (PWAs), offering a native app-like user experience that is fast and available, even when offline.
The primary performance benefit of a local database is its proximity to the application: queries and updates happen directly on the user's device, eliminating the overhead of multiple network hops. Common optimizations include:
-
-
Caching: Storing frequently accessed data in memory or on disk to minimize expensive operations.
-
Batching Writes: Grouping database operations into a single write transaction to reduce overhead and lock contention.
-
Efficient Indexing: Using appropriate indexes to speed up queries, especially important for applications that handle large data sets or frequent lookups.
-
-
These techniques ensure that local databases run smoothly, even on lower-powered or mobile devices.
Storing data on user devices introduces unique security considerations, such as the risk of physical theft or unauthorized access. Consequently, many local databases support encryption options to safeguard sensitive information. Developers can implement additional security measures like device-level encryption, secure storage plugins, and user authentication to further protect data from prying eyes.
-
-
-
Why RxDB is Optimized for JavaScript Applications
-
RxDB (Reactive Database) is an offline-first, NoSQL database designed to meet the needs of modern JavaScript applications. Built with a focus on reactivity and real-time data handling, RxDB excels in scenarios where low-latency, offline availability, and scalability are essential.
At the core of RxDB is reactive programming, allowing you to subscribe to changes in your data collections and receive immediate UI updates when records change - no manual polling or refetching required. For instance, a chat application can display incoming messages as soon as they arrive, maintaining a smooth and responsive experience.
RxDB's primary design goal is to work seamlessly in offline environments. Even if your device loses internet connectivity, RxDB enables you to continue reading and writing data. Once the connection is restored, all pending changes are automatically synchronized with your backend. This offline-first approach is ideal for productivity apps, field service tools, and other scenarios where reliability and user autonomy are paramount.
Rather than relying on implicit data models, RxDB leverages JSON schema to define document structures. This approach promotes data consistency by enforcing constraints such as required fields and acceptable data formats. As your application grows and changes, RxDB's built-in schema versioning and migration tools help you evolve your database schema safely, minimizing risks of data corruption or loss.
With lazy loading of data and the ability to utilize efficient storage engines, RxDB delivers high-speed operations and quick response times. By minimizing disk I/O and leveraging indexes effectively, RxDB ensures that even large-scale applications remain performant. Its reactive nature also helps avoid unnecessary re-renders, improving the end-user experience.
RxDB is battle-tested in production environments, handling use cases from small single-user applications to large-scale enterprise solutions. Its robust replication mechanism resolves conflicts, manages concurrent writes, and ensures data integrity. The active open-source community provides ongoing support, documentation updates, and feature improvements.
Ultimately, RxDB is more than just a database - it's a robust, reactive toolkit that empowers you to build fast, resilient, and user-centric applications. Whether you're creating an offline-first note-taking app or a real-time collaborative platform, RxDB can handle your local storage needs with ease and flexibility.
-
-
\ No newline at end of file
diff --git a/docs/articles/local-database.md b/docs/articles/local-database.md
deleted file mode 100644
index a9c544cbabd..00000000000
--- a/docs/articles/local-database.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications
-
-> An in-depth exploration of local databases and why RxDB excels as a local-first solution for JavaScript applications.
-
-# What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications
-
-A **local database** is a data storage system residing on a user's device, allowing applications to store, query, and manipulate information without needing continuous network access. This approach prioritizes quick data retrieval, efficient updates, and the ability to function in [offline-first](../offline-first.md) scenarios. In contrast, server-based databases require an active internet connection for each request and response cycle, making them more vulnerable to latency, network disruptions, and downtime.
-
-Local databases often leverage technologies such as **IndexedDB**, **SQLite**, or **WebSQL** (though WebSQL has been deprecated). These technologies manage both structured data (like relational tables) and unstructured data (such as JSON documents). When connectivity is restored, local databases typically sync their changes back to a central server-side database, maintaining consistent and up-to-date records across multiple devices.
-
-### Use Cases of Local Databases
-
-Local databases are particularly beneficial for:
-
-- **Offline Functionality**: Essential for apps that must remain usable without a consistent internet connection, such as note-taking apps or offline-first CRMs. Users can continue adding and editing data, then sync changes once they reconnect.
-- **Low Latency**: By reducing round-trips to remote servers, local databases enable real-time responsiveness. This feature is critical for interactive applications such as gaming platforms, data dashboards, or analytics tools that need near-instant feedback.
-- **Data Synchronization**: Many modern applications - like chat systems or collaborative editing tools - require continuous data exchange between multiple users or devices. Local databases can handle intermittent connectivity gracefully, queuing updates locally and syncing them when possible.
-
-In addition, local databases are increasingly integral to **Progressive Web Apps (PWAs)**, offering a native app-like user experience that is fast and available, even when offline.
-
-### Performance Optimization
-
-The primary performance benefit of a local database is its proximity to the application: queries and updates happen directly on the user's device, eliminating the overhead of multiple network hops. Common optimizations include:
-
-- **Caching**: Storing frequently accessed data in memory or on disk to minimize expensive operations.
-- **Batching Writes**: Grouping database operations into a single write transaction to reduce overhead and lock contention.
-- **Efficient Indexing**: Using appropriate indexes to speed up queries, especially important for applications that handle large data sets or frequent lookups.
-
-These techniques ensure that local databases run smoothly, even on lower-powered or [mobile devices](./mobile-database.md).
-
-
-
-### Security and Encryption
-
-Storing data on user devices introduces unique security considerations, such as the risk of physical theft or unauthorized access. Consequently, many local databases support **encryption** options to safeguard sensitive information. Developers can implement additional security measures like **device-level encryption**, **secure storage plugins**, and user authentication to further protect data from prying eyes.
-
----
-
-
-
-
-
-
-
-## Why RxDB is Optimized for JavaScript Applications
-
-RxDB (Reactive Database) is an offline-first, NoSQL database designed to meet the needs of modern JavaScript applications. Built with a focus on reactivity and real-time data handling, RxDB excels in scenarios where low-latency, offline availability, and scalability are essential.
-
-### Real-Time Reactivity
-
-At the core of RxDB is **reactive programming**, allowing you to subscribe to changes in your data collections and receive immediate UI updates when records change - no manual polling or refetching required. For instance, a chat application can display incoming messages as soon as they arrive, maintaining a smooth and responsive experience.
-
-
-
-### Offline-First Support
-
-RxDB's primary design goal is to work seamlessly in offline environments. Even if your device loses internet connectivity, RxDB enables you to continue reading and writing data. Once the connection is restored, all pending changes are automatically synchronized with your backend. This [offline-first approach](../offline-first.md) is ideal for productivity apps, field service tools, and other scenarios where reliability and user autonomy are paramount.
-
-### Flexible Data Replication
-
-A standout feature of RxDB is its [bi-directional replication](../replication.md). It supports synchronization with a variety of backends, such as:
-
-- [CouchDB](../replication-couchdb.md): Via the CouchDB replication, facilitating easy integration with any Couch-compatible server.
-- [GraphQL Endpoints](../replication-graphql.md): Through community plugins, developers can replicate JSON documents to and from GraphQL servers.
-- [Custom Backends](../replication-http.md): RxDB provides hooks to build custom replication strategies for proprietary or specialized server APIs.
-
-This flexibility ensures that RxDB fits into diverse architectures without locking you into a single vendor or technology stack.
-
-### Schema Validation and Versioning
-
-Rather than relying on implicit data models, RxDB leverages **JSON schema** to define document structures. This approach promotes data consistency by enforcing constraints such as required fields and acceptable data formats. As your application grows and changes, RxDB's built-in **schema versioning** and migration tools help you evolve your database schema safely, minimizing risks of data corruption or loss.
-
-### Rich Plugin Ecosystem
-
-One of RxDB's greatest strengths is its **pluggable architecture**, allowing you to add functionality as needed:
-
-- [Encryption](../encryption.md): Secure your data at rest using advanced encryption plugins.
-- [Full-Text Search](../fulltext-search.md): Integrate powerful text search capabilities for applications that require quick and flexible query options.
-- [Storage Adapters](../rx-storage.md): Swap out the underlying storage layer (e.g., [IndexedDB in the browser](../rx-storage-indexeddb.md), [SQLite](../rx-storage-sqlite.md) in [React Native](../react-native-database.md), or a custom engine) without rewriting your application logic.
-
-You can fine-tune RxDB to your exact needs, avoiding the performance overhead of unnecessary features.
-
-### Multi-Platform Compatibility
-
-RxDB is a perfect fit for cross-platform development, as it supports numerous environments:
-
-- **Browsers (IndexedDB)**: For web and PWA projects.
-- **Node.js**: Ideal for server-side rendering or background services.
-- **React Native**: Leverage SQLite or other adapters for mobile app development.
-- [Electron](../electron-database.md): Create offline-capable desktop apps with a unified codebase.
-
-This versatility empowers teams to reuse application logic across multiple platforms while maintaining a consistent data model.
-
-### Performance Optimization
-
-With **lazy loading** of data and the ability to utilize efficient storage engines, RxDB delivers high-speed operations and quick response times. By minimizing disk I/O and leveraging indexes effectively, RxDB ensures that even large-scale applications remain performant. Its reactive nature also helps avoid unnecessary re-renders, improving the end-user experience.
-
-### Proven Reliability
-
-RxDB is battle-tested in production environments, handling use cases from small single-user applications to large-scale enterprise solutions. Its robust replication mechanism resolves conflicts, manages concurrent writes, and ensures data integrity. The active open-source community provides ongoing support, documentation updates, and feature improvements.
-
-### Developer-Friendly Features
-
-For developers, RxDB offers:
-
-- **Straightforward APIs**: Built on top of familiar JavaScript paradigms like promises and observables.
-- **Excellent Documentation**: Detailed guides, tutorials, and references for every major feature.
-- **Rich Community Support**: Benefit from an active ecosystem of contributors creating plugins, answering questions, and maintaining core libraries.
-
-These qualities streamline development, making RxDB an appealing choice for teams of all sizes.
-
----
-
-## Follow Up
-
-Ready to get started? Here are some next steps:
-
-- Try the [Quickstart Tutorial](../quickstart.md) and build a basic project to see RxDB in action.
-- Compare RxDB with [other local database solutions](../alternatives.md) to determine the best fit for your unique requirements.
-
-Ultimately, **RxDB** is more than just a database - it's a robust, reactive toolkit that empowers you to build fast, resilient, and user-centric applications. Whether you're creating an offline-first note-taking app or a real-time collaborative platform, RxDB can handle your local storage needs with ease and flexibility.
diff --git a/docs/articles/local-first-future.html b/docs/articles/local-first-future.html
deleted file mode 100644
index 85f67613d88..00000000000
--- a/docs/articles/local-first-future.html
+++ /dev/null
@@ -1,265 +0,0 @@
-
-
-
-
-
-Why Local-First Software Is the Future and its Limitations | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Why Local-First Software Is the Future and what are its Limitations
-
Imagine a web app that behaves seamlessly even with zero internet access, provides sub-millisecond response times, and keeps most of the user's data on their device. This is the local-first or offline-first approach. Although it has been around for a while, local-first has recently become more practical because of maturing browser storage APIs and new frameworks that simplify data synchronization. By allowing data to live on the client and only syncing with a server or other peers when needed, local-first apps can deliver a user experience that is fast, resilient, and privacy-friendly.
-
However, local-first is no silver bullet. It introduces tricky distributed-data challenges like conflict resolution and schema migrations on client devices. In this article, we'll dive deep into what local-first means, why it's trending, its pros and cons, and how to implement it in real applications. We'll also discuss other tools, criticisms, backend considerations, and how local-first compares to traditional cloud-centric approaches.
In local-first software, the primary copy of your data lives on the client rather than a remote server. Rather than sending each read or write over the network, you store and manipulate data in a local database on the user’s device. Sync then happens in the background, ensuring all devices eventually converge to a consistent state.
-
This approach is increasingly popular because it leads to instant app responses (no network delay for most operations), genuine offline capability, and more direct data ownership for users. Local-first apps also sidestep outages and if the server or internet goes down, users can keep working with their local data. When connectivity returns, everything syncs. This makes the user experience more resilient and gives them control of their data, which is especially appealing when privacy concerns or limited connectivity are key factors.
-
Local-First software: A set of principles for software that enables both collaboration and ownership for users. Local-first ideals include the ability to work offline and collaborate across multiple devices, while also improving the security, privacy, long-term preservation, and user control of data.
The push for local-first is driven by a few key new technological capabilities that previously restricted client devices from running heavy local-first computing:
-
-
-
Relaxed Browser Storage Limits: In the past, true local-first web apps were not very feasible due to storage limitations in browsers. Early web storage options like cookies or localStorage had tiny limits (~5-10MB) and were unsuitable for complex data. Even IndexedDB, the structured client storage introduced over a decade ago, had restrictive quotas on many browsers: For example, older Firefox versions would prompt the user if more than 50MB was being stored. Mobile browsers often capped IndexedDB to 5MB without user permission. Such limits made it impractical to cache large application datasets on the client. However, modern browsers have dramatically increased these limits. Today, IndexedDB can typically store hundreds of megabytes to multiple gigabytes of data, depending on device capacity. Chrome allows up to ~80% of free disk space per origin (tens of GB on a desktop), Firefox now supports on the order of gigabytes per site (10% of disk size), and even Safari (historically strict) permits around 1GB per origin on iOS. In short, the storage quotas of 5-50MB are a thing of the past and modern web apps can cache very large datasets locally without hitting a ceiling. This shift in storage capabilities has unlocked new possibilities for local-first web apps that simply weren't viable a few years ago.
-
-
-
New Storage APIs (OPFS): The new Browser API Origin Private File System (OPFS), part of the File System Access API, enables near-native file I/O from within a browser. It allows web apps to manage file handles securely and perform fast, synchronous reads/writes in Web Workers. This is a huge deal for local-first computing because it makes it feasible to embed robust database engines directly in the browser, persisting data to real files on a virtual filesystem. With OPFS, you can avoid some of the performance overhead that comes with IndexedDB-based workarounds, providing a near-native speed experience for file-structured data.
-
-
-
-
Bandwidth Has Grown, But Latency Is Capped: Internet infrastructure has rapidly expanded to provide higher throughput making it possible to transfer large amounts of data more quickly. However, latency (i.e., round-trip delay) is constrained by the speed of light and other physical limitations in fiber, satellite links, and routing. We can always build out bigger "pipes" to stream or send bulk data, but we can't significantly reduce the base round-trip time for each request. This is a physical limit, not a technological one. Local-first strategies mitigate this fundamental latency limit by avoiding excessive client-server calls in interactive workflows, once data is on the client, it's instantly available for reads and writes without waiting on a network round-trip. Imagine, transferring around 100,000 "average" JSON documents might only consume about the same bandwidth as two frames of a 4K YouTube video which can be transferred in milliseconds. This shows just how far raw data throughput has come. Yet each request still has a 100-200ms latency or more, which becomes noticeable in user interactions. Local-first mitigates this by minimizing round-trip calls during active use and using the available bandwidth to directly transfer most of the data on the first app start.
-
-
-
-
WebAssembly: Another advancement is WebAssembly (WASM), which allows developers to compile low-level languages (C, C++, Rust) for execution in the browser at near-native speed. This means database engines, search algorithms, vector databases, and other performance-heavy tasks can run right on the client. However, a key limitation is that WASM cannot directly access persistent storage APIs in the browser. Instead, all data must be send from WASM to JavaScript (or the main thread) and then go through something like IndexedDB or OPFS. This extra indirection is slower compared to plain JavaScript->storage calls. Looking ahead, there might come up future APIs that allow WASM to interface with persistent storage directly, and if those land, local-first systems could see another major boost in performance.
-
-
-
Improvements in Local-First Tooling: A major factor fueling the rise of local-first architectures is the dramatic leap in client-side tooling and performance. For instance, consider a local-first email client that stores one million messages. In 2014, searching through that many documents, especially with something like early PouchDB, could take minutes in a browser. Today, with advanced offline databases like RxDB, you can use the OPFS storage with sharding across multiple web workers (one per CPU) and use memory-mapped techniques. The result is a regex search of one million of these email documents in around 120 milliseconds - all in JavaScript, running inside a standard web browser, on a mobile phone.
-
Better yet, this performance ceiling is likely to keep rising. Newer browser features and WebAssembly optimizations could enable even faster indexing and query operations, closing the gap with native desktop clients. I even experimented with GPU-accelarated queries (using WebGPU) which, while still in experimental stage, might deliver client-side performance that outperforms servers which do not have a graphics card.
-These transformations highlight why local-first has become truly practical: not only can you sync and work offline, but you can handle serious data loads with performance that would have been unthinkable just a few years ago.
Jevons' Paradox says that making a resource cheaper or more efficient to use often leads to greater overall consumption. Originally about coal, it applies to the local-first paradigm in a way where we require apps to have more features, simply because it is technically possible, the app users and developers start to expect them:
Performance & UX: Running from local storage means low latency and instantaneous interactions. There's no round-trip delay for most operations. Local-first apps aim to provide near-zero latency responses by querying a local database instead of waiting for a server response. This results in a snappy UX (often no need for loading spinners) because data reads/writes happen immediately on-device. Modern users expect real-time feedback, and local-first delivers that by default.
-
-
-
User Control & Privacy: Storing data locally can limit how much sensitive information is sent off to remote servers. End users have greater control over their data, and the app can implement client-side encryption, thereby reducing the risk of mass data breaches. Its even possible to only replicated encrypted data with a server so that the backend does not know about the data at all and just acts as a backup/replication endpoint.
-
-
-
Offline Resilience: Obviously, being able to work offline is a major benefit. Users can continue using the app with no internet (or flaky connectivity), and their changes sync up once online. This is increasingly important not just for remote areas, but for any app that needs to be available 24/7. Even though mobile networks have improved, connectivity can still drop; local-first ensures the app doesn't grind to a halt. The app "stores data locally at the client so that it can still access it when the internet goes away."
-
-
-
-
Realtime Apps: Today's users expect data to stay in sync across browser tabs and devices without constant page reloads. In a typical cloud app, if you want real-time updates (say to show that a friend edited a document), you'd need to implement a websocket or polling system for the server to push changes to clients, which is complex. Local-first architectures naturally lend themselves to realtime-by-default updates because the application state lives in a local database that can be observed for changes. Any edits (local or incoming from the server) immediately trigger UI updates. Similarly, background sync mechanisms ensure that new server-side data flows into the local store and into the user interface right away, no need to hit F5 to fetch the latest changes like on a traditional webpage.
Reduced Server Load: Because local-first architectures typically transfer large chunks of data once (e.g., during an initial sync) and then sync only small diffs (delta changes) afterward, the server does not have to handle repeated requests for the same dataset. This bulk-first, diff-later approach drastically decreases the total number of round-trip requests to the backend. In scenarios where hundreds of simultaneous users each require continuous data access, an offline-ready client that only periodically sends or receives changes can scale more efficiently, freeing your servers to handle more users or other tasks. Instead of being bombarded with frequent small queries and updates, the server focuses on periodic sync operations, which can be more easily optimized or batched. It Scales with Data, Not Load. In fact for most type of apps, most of the data itself rarely changes. Imagine a CRM system, how often does the data of a customer really change compared to how of a user open the customer-overview page which would load data from a server in traditional systems?
-
-
-
Less Need for Custom API Endpoints: A local-first architecture often simplifies backend design. Instead of writing extensive REST routes for each client operation (create, read, update, delete, etc.), you can build a single replication endpoint or a small set of endpoints to handle data synchronization for each entity. The client manages local data, merges edits, and pushes/pulls changes with the server automatically. This not only reduces boilerplate code on the backend but also frees developers to focus on business logic and domain-specific concerns rather than spending time creating and maintaining dozens of narrowly scoped endpoints. As a result, the overall system can be easier to scale and maintain, delivering a smoother developer experience.
-
-
-
Simplified State Management in Frontend: Because the local database holds the authoritative state, you might rely less on complex state management libraries (Redux, MobX, etc.). The DB becomes a single source of truth for your UI. In an offline-first app, your global state is already there in a single place stored inside of the local database, so you don't need as many in-memory state layers to synchronize. The UI can directly bind to the database (using queries or reactive subscriptions). All sources of changes (user input, remote updates, other tabs) funnel through the database. This can significantly reduce the "glue code" to keep UI state in sync with server state because the local DB does that for you. With Local-First tools, "you might not need Redux" because the reactive DB fulfills that role of state management already.
-
-
-
Observable Queries: One of the big advantages of storing data locally is the ability to subscribe to data changes in real time, often called observable queries. When the data changes - either from the user's actions or from a remote sync - the local database automatically updates the subscribed query results, and the UI can redraw without a manual refresh. This reactive pattern can make apps feel much more live and responsive.
-
In the beginnings this was mostly done by watching a changes feed (like in PouchDB) and re-running queries whenever data changed. However, this early approach was slow and didn't scale well, because the entire query had to be recalculated each time. Later, RxDB introduced the EventReduce Algorithm, which merges incoming document changes into an existing query result by using a big binary decision tree. With this, updated query results can be "calculated" on the CPU instead of re-running them over the database. This makes query updates almost instantaneous and scales better as your data grows. Nowadays, many local databases have added similar features: for example, dexie.js introduced liveQuery, letting developers build real-time UIs without repeatedly scanning the entire dataset.
-
-
-
Better Multi-Tab and Multi-Device Consistency: Because the source of truth is on the client, if the user has the app open in multiple tabs or even multiple devices, each has a full copy of the data. In browsers, many offline databases use a storage like IndexedDB that is shared across tabs of the same origin. This means all tabs see the same up-to-date local state. For example, if a user logs in or adds data in one tab, the other tab can automatically reflect that change via the shared local DB and events. This solves a common issue in web apps where one tab doesn't know that something changed in another tab. With local-first, multi-tab just works by default because there's "exactly one state of the data across all tabs". Similarly, on multiple devices, once sync runs, each device eventually converges to the same state.
-
-
If your users have to press F5 all the time, your app is broken!
-
-
-
-
Potential for P2P and Decentralization: While most current local-first apps still use a central backend for syncing, the paradigm opens the door to peer-to-peer data syncing. Because each device has the full data, devices could sync directly via LAN or other P2P channels for collaboration, reducing reliance on central servers. There are experimental frameworks that allow truly decentralized sync. This is a more advanced benefit, but it aligns with the ethos of giving users more control and reducing dependence on any one cloud provider.
-
-
-
These advantages show why developers are excited about local-first. You get happier users thanks to a fast, offline-capable app, and you can differentiate your product by working in scenarios where others fail (e.g. poor connectivity). Companies like Google, Amazon, etc., invest heavily in reducing latency and adding offline modes for a reason: it improves retention and usability. Local-first design takes that to the extreme by default.
However, this approach is not without significant challenges. It's important to understand the drawbacks and trade-offs before deciding to go all-in on local-first. Let's examine the flip side.
-
You fully understood a technology when you know when not to use it
Critics of local-first approaches often point out these challenges. Here's a comprehensive list of cons, criticisms, and obstacles associated with local-first development and proposed solutions on how to solve these obstacles:
-
-
Data Synchronization: Data synchronization is arguably the hardest part of local-first development, because when every user's device can be offline for extended periods, data inevitably diverges. Ensuring those changes propagate and reconcile with minimal user headaches is a major challenge in distributed systems. Two main approaches have emerged:
-
-
-
Use a bundled frontend+backend solution where the backend is tightly coupled to the client SDK and knows exactly how to handle sync. A common example is Firestore (part of the Firebase ecosystem) where Google's servers and client libraries collectively manage storage, change detection, conflict resolution, and syncing. The upside is you have a turnkey solution, developers can focus on features rather than writing sync logic. The downside is lock-in, because the sync protocol is proprietary and tailored to that vendor. In many organizations, this is a non-starter: existing company infrastructure can't be uprooted or replaced just for a single offline-capable app. This lock-in issue can arise even if the backend is not strictly a third-party vendor but simply another technology like PostgreSQL, because it still forces you to consolidate all your data into a single system that you might not use otherwise.
-
-
-
Custom Replication with Your Own Endpoints: Alternatively, tools like RxDB allow you to implement your own replication endpoints on top of your existing infrastructure. This is the approach relies on a lightweight, git-like Sync Engine. The server remains relatively "dumb," focusing on storing revisions, tracking changes, and marking conflicts, while the client library does the actual conflict resolution. During sync, if the server detects a conflict (e.g., two offline edits to the same document), it notifies the client, which then decides how to merge them, whether via last-write-wins, a custom merge function, or a CRDT. Setting up custom endpoints does require more development effort, but you avoid vendor lock-in and can integrate seamlessly with your existing database(s). Your system simply needs to support incrementally fetching changes (pull) and accepting local modifications (push), which can be layered on top of nearly any data store or architecture.
-
Tools which support "any backend" are of course harder to monetize because they cannot sell SaaS services or a Cloud Subscription which is why most tools use a fixed backend instead of an open Sync Engine.
-
-
-
-
-
-
-
Conflict Resolution: When multiple offline edits happen on the same data, you inevitably get merge conflicts. For example, if two users (or the same user on two devices) both edit the same document offline, when both sync, whose changes win? Local-first systems need a conflict resolution strategy. Some systems use last-write-wins (firestore) or deterministic revision hashing to pick a "winner" (as in CouchDB/PouchDB). This is simple but may drop one user's changes. Other approaches keep both versions and merge them either via an implement "merge-function" or require a manual merge step (e.g., like git conflicts or showing the user a diff UI). More advanced solutions involve CRDTs (Conflict-free Replicated Data Types) which mathematically merge changes (used for rich text collaboration, for instance). Libraries like Automerge or Yjs implement CRDTs to "magically solve conflicts". But in practice, using CRDTs is also complex and has its own trade-offs and sometimes not even possible like when you need additional data from another instance for a "correct" merge. No matter which route, handling conflicts adds complexity to your app logic or infrastructure. In cloud-based (online-first) apps, you avoid this because everyone is always editing the single up-to-date copy on the server. Local-first shifts that burden to the client side.
-
Here is an example on how a client-side merge functions works in RxDB:
-
import { deepEqual } from 'rxdb/plugins/utils';
-export const myConflictHandler = {
- /**
- * isEqual() is used to detect if two documents are
- * equal. This is used internally to detect conflicts.
- */
- isEqual(a, b) {
- /**
- * isEqual() is used to detect conflicts or to detect if a
- * document has to be pushed to the remote.
- * If the documents are deep equal,
- * we have no conflict.
- * Because deepEqual is CPU expensive,
- * on your custom conflict handler you might only
- * check some properties, like the updatedAt time or revision-strings
- * for better performance.
- */
- return deepEqual(a, b);
- },
- /**
- * resolve() a conflict. This can be async so
- * you could even show an UI element to let your user
- * resolve the conflict manually.
- */
- async resolve(i) {
- /**
- * In this example we drop the local state and use the server-state.
- * This basically implements a "first-on-server-wins" strategy.
- *
- * In your custom conflict handler you could want to merge properties
- * of the i.realMasterState, i.assumedMasterState and i.newDocumentState
- * or return i.newDocumentState to have a "last-write-wins" strategy.
- */
- return i.realMasterState;
- }
-};
-
-
-
-
-
Eventual Consistency (No Single Source of Truth): A local-first system is eventually consistent by nature. There is no single authoritative copy of the data at all times. Instead you have one per device (and maybe one on the server), and they sync to become consistent eventually. This means at any given moment, two users might not see the same data if one hasn't synced recently. Users could even make decisions based on stale data. In many applications this is acceptable (the data will catch up), but for some scenarios it's problematic. For instance, an offline-first banking app that lets you initiate a money transfer offline could be dangerous if the account balance was out-of-date or if the transfer needs immediate consistency. Essentially, not all apps can tolerate eventual consistency. If your use case demands strong consistency (e.g., inventory systems where overselling is a big issue, or real-time collaborative editing where every keystroke must be seen by others instantly), a purely local-first approach might need augmentation or may not fit.
-
-
-
Initial Data Load and Data Size Limits: Local-first requires pulling data down to the client. If your dataset is huge (gigabytes), it's simply not feasible to download everything to every client. For example, syncing every tweet on Twitter to every user's phone is impossible. Local-first works best when the data set per user is reasonably sized (up to 2 Gigabytes). In practice, you often limit the data to just that user's own data or a subset relevant to them. Even then, on first use the app might need to download a significant chunk of data to initialize the local database. There is a upper bound on dataset size beyond which the initial sync or storage needs become impractical. You cannot assume unlimited local storage. If your data is too large, local-first will either fail or you'll need to only sync partial data (and then handle what happens if the needed data isn't present locally). In short, local-first is unsuitable for massive datasets or data that cannot be partitioned per user.
-
-
-
-
Storage Persistence (Browser Limitations): Storing data in the browser (via IndexedDB or similar) is not as durable as on a server disk. Browsers may evict data to save space (especially on mobile devices). For instance, Safari notoriously wipes out IndexedDB data if the user hasn't used the site in ~7 days. Other browsers have their own eviction policies for offline data. Also, users can at any time clear their browser storage (intentionally or via something like "Clear site data"). This means the local data cannot be 100% trusted to stay forever. A well-behaved local-first app needs to be able to recover if local data is lost, usually by pulling from the server again. Essentially, the server still often serves as a backup. But if your app had any purely local data (not intended to sync), that's at risk. Mobile apps (with SQLite or filesystem storage) are a bit more stable than web browsers, but even there, uninstalls or certain OS actions can remove local data. This is a challenge: How to cache data offline for speed while ensuring if it's wiped, the user doesn't lose everything important. Cloud-only apps by contrast keep data in the cloud so it's typically safe unless the server fails (and servers are easier to backup reliably).
-
-
-
-
Complex Client-Side Logic & Increased App Size: A local-first app tends to be more complex on the client side. You're essentially putting what used to be server responsibilities (storage, query engine, sync logic) into the frontend. This can increase the size of your frontend bundle (including a database library, possibly CRDT or sync code, etc.). It also increases memory and CPU usage on the client, as the browser/phone is doing more work. Low-end devices or older phones might struggle if the app is not optimized. Developers need to consider performance tuning for the local database (indexing, query efficiency) just like they would on a server. So while the user gains benefits, the app developer has to manage this complexity.
-
-
-
Performance Constraints in JavaScript: Even though devices are fast, a local JS database is generally slower than a server DB on robust hardware. There are many layers (JS -> IndexedDB -> possibly SQLite under the hood) that add overhead. For example, inserting a record might go through the DB library, the storage engine, the browser's implementation, down to disk. For most UI uses this is fine (you don't need 10k writes/sec in a to-do app, you need maybe a few writes per second at most). But if your app does heavy data processing, the browser might become a bottleneck.
-
The key question is "Is it fast enough?". Often the answer is yes for typical usage, but developers must be mindful of not doing something on the client that truly requires big iron servers. For instance, full-text indexing of a million documents might be too slow in a client-side DB. Unpredictable performance is also a factor: Different users have different devices. A query that takes 50ms on a high-end desktop might take 500ms on a low-end phone in battery saving mode. So performance tuning and testing across devices is needed, and some heavy tasks might still belong on the server side. For example if you build a local vector database you might want to create the embeddings on the server and sync them instead of creating them on the client.
-
-
-
Client Database Migrations: As your app evolves, you'll change data models or add new fields. In a cloud-first app, you'd typically run a migration on the server database. In a local-first app, you have not only the server DB (if any) but also every client's local database to consider. Upgrading the schema means you need to write migration logic that runs on each client, perhaps the next time they launch the app after an update. Clients may be offline or not upgrade the app immediately, so you could have different versions of the schema in the wild. This complicates data handling (the sync protocol might need to handle multiple schema versions until everyone is updated). Providing a smooth migration path for local data is doable (many libraries provide migration facilities), but it requires careful testing. In a worst case, a failed migration on a client could brick the app for that user or force a full resync. This is a much bigger headache than just migrating a centralized DB at midnight while your service is in maintenance mode. 🌃
-
-
-
Security and Access Control: In cloud-based apps, enforcing data security (who can see what) is done on the server. The client only gets the data it's authorized to get. In a local-first scenario, you often need to partition data per user on the backend as well, to ensure users only sync down their own data (or data they have permission for). One simple strategy is to give each user their own database or dataset on the server and only replicate that. For example, CouchDB allows creating one database per user and replication can be scoped to that DB which makes permission handling easy. But if you ever need to query across users (say an admin view or aggregate analytics), having data split into many small DBs becomes a pain. The alternative is a single backend database with a fine-grained access control, and the client asks to sync only certain documents/fields. That usually means writing a custom sync server or using something like GraphQL with resolvers that respect permissions. In short, implementing auth and permissions in sync adds complexity. Also, any data stored on the client is theoretically vulnerable to extraction (if someone compromises the device or uses dev tools). You can encrypt local databases to prevent extraction after the server "revokes" the decryption password to mitigate the data extraction risk.
-
-
-
-
-
Relational Data and Complex Queries: Most client-side/offline databases are NoSQL/document oriented for flexibility in syncing and easy conflict handling. They may not support complex join queries or ACID transactions across multiple tables like a full SQL database would. This is partly because replicating a full relational model is much harder (maintaining referential integrity, etc., when data is partial on a client) or not even logically possible. For example if you have two offline clients running a complex UPDATE X WHERE Y FROM Z INNER JOIN Alice INNER JOIN Bob query and then they go online, you have no easy way of handling these conflicts.
-
If your app has heavy relational data requirements or relies on complex server-side queries (aggregations, multi-join reports), you might find the local database either cannot do it or is too slow to do it client-side. The lack of robust relational querying is something to plan for and you might need to adjust your data model to be more document-oriented or use client-side libraries to run joins in memory. Most tools use NoSQL because it makes replication easy and implementing true relational sync would require extremely sophisticated solutions and even needing an atomic clock for full consistency across nodes (like google spanner). So, if your app truly needs SQL power on the client, local-first might complicate things.
-
-
In Local-First, most tools use NoSQL because it makes replication and conflict handling easy.
-
-
-
-
That's a long list of challenges! In summary, local-first approaches introduce distributed data issues on the client side that web developers usually didn't have to deal with. Despite these challenges, the local-first movement is steadily growing because the benefits to user experience and data control are very compelling and modern tools are emerging to mitigate a lot of these difficulties. All of these are solved or solvable for your specific use-case, just keep them in mind before you start architecting your local-first app.
-
Local-First vs. Traditional Online-First Approaches
-
So now that you know the pros and cons about Local-First. Lets directly compare it to your previous "online-first" stack:
Local-first: Apps like WhatsApp store messages locally on your device, letting you read past messages and even compose new ones without a stable network connection. Once online, the messages sync with the server and other participants.
-
Online-first: Purely cloud-based apps typically stop functioning (beyond simple caching) when the network or server goes down. They rely on a constant connection to fetch or store user data.
Local-first: Because data operations happen on the device, you see near-instant interactions (e.g., typing and reading chats feels very responsive, as the messages are on your phone). Syncing occurs in the background without delaying the user.
-
Online-first: Most interactions involve a round-trip to the server, adding network latency and potentially requiring loading states or fallback UIs. If the network is slow or unreliable, users can experience delays when sending or receiving updates.
Local-first: Much of the complexity like storing data or handling conflicts, shifts to the client. WhatsApp, for instance, caches all messages locally and queues unsent messages offline. Once reconnected, it must reconcile with the server and other devices, especially if the same account is used on multiple platforms.
-
Online-first: A single central server manages data and concurrency, so clients remain thinner. However, the app becomes unusable if connectivity is lost, and any server downtime directly impacts all users.
Local-first: Users hold a complete copy of data on their own device, retaining control and quick access. This can raise challenges when data sets are very large; phone storage might be insufficient, or backups may be harder to guarantee.
-
Online-first: Storing all data in the cloud scales more easily, and providers often manage backups automatically. On the downside, users depend on the service and must trust it to protect their data.
Local-first: Particularly helpful for scenarios where offline operation and immediate responsiveness matter, such as chat apps (like WhatsApp), field tools in low-connectivity environments, or any use case where users can't rely on being online 24/7.
-
Online-first: Well-suited for systems that demand real-time central control or massive data aggregation with minimal offline needs, such as large-scale analytics platforms or services where guaranteed immediate global consistency is essential.
-
Hybrid: In reality, most modern apps often blend these approaches, giving users partial offline capabilities (caching, queued updates) alongside a robust central service. This hybrid method offers the benefits of local speed and resilience, while still leveraging cloud infrastructure for collaboration and global reach. But a truth local-first app is way more than just a cache.
In the early days of offline-capable web apps (around 2014), the common phrase was "Offline-First". Tools like PouchDB popularized the notion that developers should assume devices are often offline or have flaky connections, so apps must continue to work seamlessly without a network. The guiding principle was "apps should treat being online as optional." If a user has no internet access, the application's core features still function, saving or queuing data locally, and automatically synchronizing once connectivity is restored.
-
4:18
What is Offline First?
-
-
Over time, this focus on offline support evolved into the broader concept of "Local-First Software," (see Ink&Switch) emphasizing not just offline operation but also the technical underpinnings of storing data locally in the client application. While offline-first is primarily about resilience to network loss, local-first highlights ownership, privacy, and performance benefits of keeping the primary data on the user's device. Most tools these days extended the original offline-first concepts, adding real-time reactivity, custom sync, and more nuances like conflict resolution or encryption.
-
However, the term "local-first" can be confusing to non-technical audiences because many people (especially in the US) associate "local first" with community-oriented movements that encourage buying from nearby businesses or supporting local initiatives. To reduce ambiguity, it may be clearer to use "local first software" or "local first development" in your documentation and marketing materials. When creating branding or logos around local-first software, avoid using the "Google Maps Pin" as a symbol. This icon typically implies geolocation or physical locality further mixing up the notion of "location-based services" with "on-device data storage."
-
Do People Actually Use Local-First or Is It Just a Trend?
-
If we look at npm download statistics, we see that PouchDB - one of the oldest libraries for local-first apps - has about 53k downloads each week, and RxDB - a newer library - has about 22k weekly downloads. Other local-first tools often have even fewer downloads. In comparison, a popular library like react-query, which does not focus on local storage, is downloaded about 1.6 million times a week. These numbers show that local-first libraries, while used, are not as common as some of the more traditional tools.
-
One reason is that local-first is still a new idea. Many developers are used to traditional "online-first" approaches, so switching to a local database and then syncing changes later can feel unfamiliar. Developers must learn different patterns, deal with offline synchronization, and handle possible conflicts. That extra work can be a barrier to adoption which might change in the future as tooling improves.
-
While most of RxDB is open source, there are also premium plugins that help sustain RxDB as a long-term project. Because people purchase these plugins, we gains insights into how developers are using local-first features:
-
-
About half of these users mainly want offline functionality for cases such as farming equipment, mining, construction, or even a shrimp farm app.
-
The other half focus on faster, real-time UIs for to-do or reading apps, a space launch planning tool, and various dashboard apps. This range of use cases highlights both the resilience offline mode can offer and the performance boost that local databases can provide when synced in the background.
Early in the history of the web, users expected static pages. If you wanted to see new content, you reloaded the page. That was normal at the time, and nobody found it strange because everything worked that way.
-
Then, as more sites added real-time features - auto-updating feeds, live notifications, single-page apps - suddenly those older "reload-only" sites began to feel slow or outdated. Why wait for a manual refresh when real-time data was possible and readily available?
-
The same pattern is happening with local-first apps. Right now, most sites are still built around network availability. We see loading spinners whenever data is fetched, and we simply wait for the server response. As local-first experiences become commonplace - removing spinners, letting users keep working when offline, and syncing in the background - everything else will start to feel frustratingly behind. Users won't tolerate slow or blocked interactions if they've seen apps that respond instantly and remain usable offline. They'll expect that as the default and we'll likely see growing pressure on developers to eliminate those extra loading steps. For many users, the experience of immediate local writes will become not just a perk, but an expectation!
-
-
\ No newline at end of file
diff --git a/docs/articles/local-first-future.md b/docs/articles/local-first-future.md
deleted file mode 100644
index bdc3caee01c..00000000000
--- a/docs/articles/local-first-future.md
+++ /dev/null
@@ -1,553 +0,0 @@
-# Why Local-First Software Is the Future and its Limitations
-
-> Discover how local-first transforms web apps, boosts offline resilience, and why instant user feedback is becoming the new normal.
-
-import {Tabs} from '@site/src/components/tabs';
-import {Steps} from '@site/src/components/steps';
-import {QuoteBlock} from '@site/src/components/quoteblock';
-import {VideoBox} from '@site/src/components/video-box';
-
-# Why Local-First Software Is the Future and what are its Limitations
-
-Imagine a web app that behaves seamlessly even with zero internet access, provides sub-millisecond response times, and keeps most of the user's data on their device. This is the **local-first** or [offline-first](../offline-first.md) approach. Although it has been around for a while, local-first has recently become more practical because of **maturing browser storage APIs** and new frameworks that simplify **data synchronization**. By allowing data to live on the client and only syncing with a server or other peers when needed, local-first apps can deliver a user experience that is **fast, resilient**, and **privacy-friendly**.
-
-However, local-first is no silver bullet. It introduces tricky distributed-data challenges like conflict resolution and schema migrations on client devices. In this article, we'll dive deep into what local-first means, why it's trending, its pros and cons, and how to implement it in real applications. We'll also discuss other tools, criticisms, backend considerations, and how local-first compares to traditional cloud-centric approaches.
-
-## What is the Local-First Paradigm
-
-In **local-first** software, the primary copy of your data lives on the **client** rather than a remote server. Rather than sending each read or write over the network, you store and manipulate data in a [local database](./local-database.md) on the user’s device. Sync then happens in the background, ensuring all devices eventually converge to a consistent state.
-
-This approach is increasingly popular because it leads to **instant** app responses (no network delay for most operations), genuine **offline capability**, and more direct **data ownership** for users. Local-first apps also sidestep outages and if the server or internet goes down, users can keep working with their local data. When connectivity returns, everything syncs. This makes the user experience **more resilient** and gives them control of their data, which is especially appealing when privacy concerns or limited connectivity are key factors.
-
-Local-First software: A set of principles for software that enables both collaboration and ownership for users. Local-first ideals include the ability to work offline and collaborate across multiple devices, while also improving the security, privacy, long-term preservation, and user control of data.
-
-## Why Local-First is Gaining Traction
-
-The push for local-first is driven by a few key new technological capabilities that previously restricted client devices from running heavy local-first computing:
-
-- **Relaxed Browser Storage Limits**: In the past, true local-first web apps were not very feasible due to **storage limitations** in browsers. Early web storage options like cookies or [localStorage](./localstorage.md#understanding-the-limitations-of-local-storage) had tiny limits (~5-10MB) and were unsuitable for complex data. Even **IndexedDB**, the structured client storage introduced over a decade ago, had restrictive quotas on many browsers: For example, older Firefox versions would **prompt the user if more than 50MB** was being stored. Mobile browsers often capped IndexedDB to 5MB without user permission. Such limits made it impractical to cache large application datasets on the client. However, modern browsers have dramatically [increased these limits](./indexeddb-max-storage-limit.md). Today, IndexedDB can typically store **hundreds of megabytes to multiple gigabytes** of data, depending on device capacity. Chrome allows up to ~80% of free disk space per origin (tens of GB on a desktop), Firefox now supports on the order of gigabytes per site (10% of disk size), and even Safari (historically strict) permits around 1GB per origin on iOS. In short, the storage quotas of 5-50MB are a thing of the past and modern web apps can cache very large datasets locally without hitting a ceiling. This shift in storage capabilities has unlocked new possibilities for **local-first web apps** that simply weren't viable a few years ago.
-
-- **New Storage APIs (OPFS)**: The new Browser API [Origin Private File System](../rx-storage-opfs.md) (OPFS), part of the File System Access API, enables near-native file I/O from within a browser. It allows web apps to manage file handles securely and perform fast, synchronous reads/writes in Web Workers. This is a huge deal for local-first computing because it makes it feasible to embed robust database engines directly in the browser, persisting data to real files on a virtual filesystem. With OPFS, you can avoid some of the performance overhead that comes with [IndexedDB-based workarounds](../slow-indexeddb.md), providing a near-native [speed experience](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md#big-bulk-writes) for file-structured data.
-
-
- - **Bandwidth Has Grown, But Latency Is Capped**: Internet infrastructure has rapidly expanded to provide higher throughput making it possible to transfer large amounts of data more quickly. However, latency (i.e., round-trip delay) is constrained by the **speed of light** and other physical limitations in fiber, satellite links, and routing. We can always build out bigger "pipes" to stream or send bulk data, but we can't significantly reduce the base round-trip time for each request. This is a physical limit, not a technological one. Local-first strategies mitigate this fundamental latency limit by avoiding excessive client-server calls in interactive workflows, once data is on the client, it's instantly available for reads and writes without waiting on a network round-trip. Imagine, transferring **around 100,000** "average" JSON documents might only consume **about the same bandwidth as two frames of a 4K YouTube video** which can be transferred in milliseconds. This shows just how far raw data throughput has come. Yet each request still has a 100-200ms latency or more, which becomes noticeable in user interactions. Local-first mitigates this by minimizing round-trip calls during active use and using the available bandwidth to directly transfer most of the data on the first app start.
-
-- **WebAssembly**: Another advancement is **WebAssembly (WASM)**, which allows developers to compile low-level languages (C, C++, Rust) for execution in the browser at near-native speed. This means database engines, search algorithms, [vector databases](./javascript-vector-database.md), and other performance-heavy tasks can run right on the client. However, a key limitation is that **WASM cannot directly access persistent storage APIs** in the browser. Instead, all data must be send from WASM to JavaScript (or the main thread) and then go through something like IndexedDB or OPFS. This extra indirection [is slower](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md) compared to plain JavaScript->storage calls. Looking ahead, there might come up future APIs that allow WASM to interface with persistent storage directly, and if those land, local-first systems could see another major boost in [performance](../rx-storage-performance.md).
-
-- **Improvements in Local-First Tooling**: A major factor fueling the rise of local-first architectures is the **dramatic leap in client-side tooling and performance**. For instance, consider a local-first **email client** that stores **one million messages**. In 2014, searching through that many documents, especially with something like early PouchDB, could take **minutes** in a browser. Today, with advanced offline databases like **RxDB**, you can use the [OPFS storage](../rx-storage-opfs.md) with [sharding](../rx-storage-sharding.md) across multiple [web workers](../rx-storage-worker.md) (one per CPU) and use [memory-mapped](../rx-storage-memory-mapped.md) techniques. The result is a **regex search** of one million of these email documents in around **120 milliseconds** - all in JavaScript, running inside a standard web browser, on a mobile phone.
-
- Better yet, this performance ceiling is likely to keep rising. Newer browser features and **WebAssembly** optimizations could enable even faster indexing and query operations, closing the gap with native desktop clients. I even experimented with GPU-accelarated queries (using **WebGPU**) which, while still in experimental stage, might deliver client-side performance that outperforms servers which do not have a graphics card.
- These transformations highlight why local-first has become truly practical: not only can you sync and work offline, but you can handle **serious data loads** with performance that would have been unthinkable just a few years ago.
-
-## What you can expect from a Local First App
-
-[Jevons' Paradox](https://en.wikipedia.org/wiki/Jevons_paradox) says that making a _resource cheaper or more efficient to use often leads to greater overall consumption_. Originally about coal, it applies to the local-first paradigm in a way where we require apps to have more features, simply because it is technically possible, the app users and developers start to expect them:
-
-### User Experience Benefits
-
-
-- **Performance & UX:** Running from local storage means **low latency** and instantaneous interactions. There's no round-trip delay for most operations. Local-first apps aim to provide [near-zero latency](./zero-latency-local-first.md) responses by querying a local database instead of waiting for a server response. This results in a snappy UX (often no need for loading spinners) because data reads/writes happen immediately on-device. Modern users expect real-time feedback, and local-first delivers that by default.
-
-- **User Control & Privacy:** Storing data locally can limit how much sensitive information is sent off to remote servers. End users have greater control over their data, and the app can implement [client-side encryption](../encryption.md), thereby reducing the risk of mass data breaches. Its even possible to only replicated encrypted data with a server so that the backend does not know about the data at all and just acts as a backup/replication endpoint.
-
-
- - **Offline Resilience:** Obviously, being able to work offline is a major benefit. Users can continue using the app with no internet (or flaky connectivity), and their changes sync up once online. This is increasingly important not just for remote areas, but for any app that needs to be available 24/7. Even though mobile networks have improved, connectivity can still drop; local-first ensures the app doesn't grind to a halt. The app _"stores data locally at the client so that it can still access it when the internet goes away."_
-
-- **Realtime Apps**: Today's users expect data to stay in sync across browser tabs and devices without constant page reloads. In a typical cloud app, if you want real-time updates (say to show that a friend edited a document), you'd need to implement a [websocket or polling](./websockets-sse-polling-webrtc-webtransport.md) system for the server to push changes to clients, which is complex. Local-first architectures naturally lend themselves to realtime-by-default updates because the application state lives in a local database that can be observed for changes. Any edits (local or incoming from the server) immediately trigger [UI updates](./optimistic-ui.md). Similarly, background sync mechanisms ensure that new server-side data flows into the local store and into the user interface right away, no need to hit F5 to fetch the latest changes like on a traditional webpage.
-
-
-
-
-
-### Developer Experience Benefits
-
-- **Reduced Server Load**: Because local-first architectures typically **transfer large chunks of data once** (e.g., during an initial sync) and then sync only small diffs (delta changes) afterward, the server does not have to handle repeated requests for the same dataset. This bulk-first, diff-later approach drastically decreases the total number of round-trip requests to the backend. In scenarios where hundreds of simultaneous users each require continuous data access, an offline-ready client that only periodically sends or receives changes can scale more efficiently, freeing your servers to handle more users or other tasks. Instead of being bombarded with frequent small queries and updates, the server focuses on periodic sync operations, which can be more easily optimized or batched. It **Scales with Data, Not Load**. In fact for most type of apps, most of the data itself rarely changes. Imagine a CRM system, how often does the data of a customer really change compared to how of a user open the customer-overview page which would load data from a server in traditional systems?
-
-- **Less Need for Custom API Endpoints**: A local-first architecture often simplifies backend design. Instead of writing extensive REST routes for each client operation (create, read, update, delete, etc.), you can build a **single replication endpoint** or a small set of endpoints to handle data synchronization for each entity. The client manages local data, merges edits, and pushes/pulls changes with the server automatically. This not only **reduces boilerplate code** on the backend but also **frees developers** to focus on business logic and domain-specific concerns rather than spending time creating and maintaining dozens of narrowly scoped endpoints. As a result, the overall system can be easier to scale and maintain, delivering a **smoother developer experience**.
-
-- **Simplified State Management in Frontend**: Because the local database holds the authoritative state, you might rely less on complex state management libraries (Redux, MobX, etc.). The DB becomes a single source of truth for your UI. In an offline-first app, your global state is already there in a single place stored inside of the local database, so you don't need as many in-memory state layers to synchronize. The UI can directly bind to the database (using queries or reactive subscriptions). All sources of changes (user input, remote updates, other tabs) funnel through the database. This can significantly reduce the "glue code" to keep UI state in sync with server state because the local DB does that for you. With Local-First tools, "you might not need Redux" because the reactive DB fulfills that role of state management already.
-
-- **Observable Queries**: One of the big advantages of storing data locally is the ability to **subscribe** to data changes in real time, often called **observable queries**. When the data changes - either from the user's actions or from a remote sync - the local database automatically updates the subscribed query results, and the UI can redraw without a manual refresh. This reactive pattern can make apps feel much more live and responsive.
-
- In the beginnings this was mostly done by watching a changes feed (like in PouchDB) and **re-running queries** whenever data changed. However, this early approach was **slow and didn't scale well**, because the entire query had to be recalculated each time. Later, RxDB introduced the [EventReduce Algorithm](https://github.com/pubkey/event-reduce), which merges incoming document changes into an existing query result by using a big [binary decision tree](https://github.com/pubkey/binary-decision-diagram). With this, updated query results can be "calculated" on the CPU instead of re-running them over the database. This makes query updates almost instantaneous and scales better as your data grows. Nowadays, many local databases have added similar features: for example, **dexie.js** introduced `liveQuery`, letting developers build real-time UIs without repeatedly scanning the entire dataset.
-
-- **Better Multi-Tab and Multi-Device Consistency**: Because the source of truth is on the client, if the user has the app open in multiple tabs or even multiple devices, each has a full copy of the data. In browsers, many offline databases use a storage like IndexedDB that is shared across tabs of the same origin. This means all tabs see the same up-to-date local state. For example, if a user logs in or adds data in one tab, the other tab can automatically reflect that change via the shared local DB and events. This solves a common issue in web apps where one tab doesn't know that something changed in another tab. With local-first, **multi-tab just works** by default because there's "exactly one state of the data across all tabs". Similarly, on multiple devices, once sync runs, each device eventually converges to the same state.
-
- > If your users have to press F5 all the time, your app is broken!
-
-- **Potential for P2P and Decentralization**: While most current local-first apps still use a central backend for syncing, the paradigm opens the door to [peer-to-peer data syncing](../replication-webrtc.md). Because each device has the full data, devices could sync directly via LAN or other P2P channels for collaboration, reducing reliance on central servers. There are experimental frameworks that allow truly decentralized sync. This is a more advanced benefit, but it aligns with the ethos of giving users more control and reducing dependence on any one cloud provider.
-
-These advantages show why developers are excited about local-first. You get happier users thanks to a fast, offline-capable app, and you can differentiate your product by working in scenarios where others fail (e.g. poor connectivity). Companies like Google, Amazon, etc., invest heavily in reducing latency and adding offline modes for a reason: it improves retention and usability. Local-first design takes that to the extreme by default.
-
-## Challenges and Limitations of Local-First
-
-However, this approach is not without significant challenges. It's important to understand the drawbacks and trade-offs before deciding to go all-in on local-first. Let's examine the flip side.
-
-You fully understood a technology when you know when not to use it
-
-Critics of local-first approaches often point out these challenges. Here's a comprehensive list of cons, criticisms, and obstacles associated with local-first development and proposed solutions on how to solve these obstacles:
-
-- **Data Synchronization**: Data synchronization is arguably the hardest part of local-first development, because when every user's device can be offline for extended periods, data inevitably diverges. Ensuring those changes propagate and reconcile with minimal user headaches is a major challenge in distributed systems. Two main approaches have emerged:
- - **Use a bundled frontend+backend solution** where the backend is tightly coupled to the client SDK and knows exactly how to handle sync. A common example is Firestore (part of the Firebase ecosystem) where Google's servers and client libraries collectively manage storage, change detection, conflict resolution, and syncing. The upside is you have a turnkey solution, developers can focus on features rather than writing sync logic. The downside is lock-in, because the sync protocol is proprietary and tailored to that vendor. In many organizations, this is a non-starter: existing company infrastructure can't be uprooted or replaced just for a single offline-capable app. This lock-in issue can arise even if the backend is not strictly a third-party vendor but simply another technology like PostgreSQL, because it still forces you to consolidate all your data into a single system that you might not use otherwise.
-
- - **Custom Replication with Your Own Endpoints**: Alternatively, tools like RxDB allow you to implement your own replication endpoints on top of your existing infrastructure. This is the approach relies on a lightweight, git-like [Sync Engine](../replication.md). The server remains relatively "dumb," focusing on storing revisions, tracking changes, and marking conflicts, while the client library does the actual [conflict resolution](../transactions-conflicts-revisions.md). During sync, if the server detects a conflict (e.g., two offline edits to the same document), it notifies the client, which then decides how to merge them, whether via last-write-wins, a custom merge function, or a [CRDT](../crdt.md). Setting up custom endpoints does require more development effort, but you avoid vendor lock-in and can integrate seamlessly with your existing database(s). Your system simply needs to support incrementally fetching changes (pull) and accepting local modifications (push), which can be layered on top of nearly any data store or architecture.
-
- Tools which support "any backend" are of course harder to monetize because they cannot sell SaaS services or a Cloud Subscription which is why most tools use a fixed backend instead of an open Sync Engine.
-
-
-- **Conflict Resolution**: When multiple offline edits happen on the same data, you inevitably get **merge conflicts**. For example, if two users (or the same user on two devices) both edit the same document offline, when both sync, whose changes win? Local-first systems need a conflict resolution strategy. Some systems use **last-write-wins** (firestore) or deterministic revision hashing to pick a "winner" (as in CouchDB/PouchDB). This is simple but may drop one user's changes. Other approaches keep both versions and merge them either via an implement ["merge-function"](../transactions-conflicts-revisions.md#custom-conflict-handler) or require a **manual merge** step (e.g., like git conflicts or showing the user a diff UI). More advanced solutions involve **CRDTs (Conflict-free Replicated Data Types)** which mathematically merge changes (used for rich text collaboration, for instance). Libraries like Automerge or Yjs implement CRDTs to "magically solve conflicts". But in practice, using CRDTs is also complex and has its own trade-offs and sometimes not even possible like when you need additional data from another instance for a "correct" merge. No matter which route, handling conflicts adds complexity to your app logic or infrastructure. In cloud-based (online-first) apps, you avoid this because everyone is always editing the single up-to-date copy on the server. Local-first shifts that burden to the client side.
-
- Here is an example on how a client-side merge functions works in RxDB:
-
- ```ts
- import { deepEqual } from 'rxdb/plugins/utils';
- export const myConflictHandler = {
- /**
- * isEqual() is used to detect if two documents are
- * equal. This is used internally to detect conflicts.
- */
- isEqual(a, b) {
- /**
- * isEqual() is used to detect conflicts or to detect if a
- * document has to be pushed to the remote.
- * If the documents are deep equal,
- * we have no conflict.
- * Because deepEqual is CPU expensive,
- * on your custom conflict handler you might only
- * check some properties, like the updatedAt time or revision-strings
- * for better performance.
- */
- return deepEqual(a, b);
- },
- /**
- * resolve() a conflict. This can be async so
- * you could even show an UI element to let your user
- * resolve the conflict manually.
- */
- async resolve(i) {
- /**
- * In this example we drop the local state and use the server-state.
- * This basically implements a "first-on-server-wins" strategy.
- *
- * In your custom conflict handler you could want to merge properties
- * of the i.realMasterState, i.assumedMasterState and i.newDocumentState
- * or return i.newDocumentState to have a "last-write-wins" strategy.
- */
- return i.realMasterState;
- }
- };
- ```
-
-- **Eventual Consistency (No Single Source of Truth):** A local-first system is **eventually consistent** by nature. There is no single authoritative copy of the data at all times. Instead you have one per device (and maybe one on the server), and they sync to become consistent eventually. This means at any given moment, two users might not see the same data if one hasn't synced recently. Users could even make decisions based on stale data. In many applications this is acceptable (the data will catch up), but for some scenarios it's problematic. For instance, an offline-first banking app that lets you initiate a money transfer offline could be dangerous if the account balance was out-of-date or if the transfer needs immediate consistency. Essentially, **not all apps can tolerate eventual consistency**. If your use case demands strong consistency (e.g., inventory systems where overselling is a big issue, or real-time collaborative editing where every keystroke must be seen by others instantly), a purely local-first approach might need augmentation or may not fit.
-
-- **Initial Data Load and Data Size Limits:** Local-first requires pulling data **down to the client**. If your dataset is huge (gigabytes), it's simply not feasible to download everything to every client. For example, syncing every tweet on Twitter to every user's phone is impossible. Local-first works best when the data set per user is reasonably sized (up to 2 Gigabytes). In practice, you often **limit the data** to just that user's own data or a subset relevant to them. Even then, on first use the app might need to download a significant chunk of data to initialize the local database. There is a **upper bound on dataset size** beyond which the initial sync or storage needs become impractical. You cannot assume unlimited local storage. If your data is too large, local-first will either fail or you'll need to only sync partial data (and then handle what happens if the needed data isn't present locally). In short, **local-first is unsuitable for massive datasets** or data that cannot be partitioned per user.
-
-
-- **Storage Persistence (Browser Limitations):** Storing data in the browser (via IndexedDB or similar) is not as durable as on a server disk. Browsers may **evict data** to save space (especially on mobile devices). For instance, Safari notoriously wipes out IndexedDB data if the user hasn't used the site in ~7 days. Other browsers have their own eviction policies for offline data. Also, users can at any time clear their browser storage (intentionally or via something like "Clear site data"). This means the local data **cannot be 100% trusted to stay forever**. A well-behaved local-first app needs to be able to recover if local data is lost, usually by pulling from the server again. Essentially, the server still often serves as a backup. But if your app had any purely local data (not intended to sync), that's at risk. **Mobile apps** (with SQLite or filesystem storage) are a bit more stable than web browsers, but even there, uninstalls or certain OS actions can remove local data. This is a challenge: How to cache data offline for speed while ensuring if it's wiped, the user doesn't lose everything important. Cloud-only apps by contrast keep data in the cloud so it's typically safe unless the server fails (and servers are easier to backup reliably).
-
-- **Complex Client-Side Logic & Increased App Size**: A local-first app tends to be more complex on the client side. You're essentially putting what used to be server responsibilities (storage, query engine, sync logic) into the frontend. This can increase the size of your frontend bundle (including a database library, possibly CRDT or sync code, etc.). It also increases memory and CPU usage on the client, as the browser/phone is doing more work. Low-end devices or older phones might struggle if the app is not optimized. Developers need to consider performance tuning for the local database (indexing, query efficiency) just like they would on a server. So while the user gains benefits, the app developer has to manage this complexity.
-
-- **Performance Constraints in JavaScript:** Even though devices are fast, a local JS database is generally **slower than a server DB** on robust hardware. There are many layers (JS -> IndexedDB -> possibly SQLite under the hood) that add overhead. For example, inserting a record might go through the DB library, the storage engine, the browser's implementation, down to disk. For most UI uses this is fine (you don't need 10k writes/sec in a to-do app, you need maybe a few writes per second at most). But if your app does heavy data processing, the browser might become a bottleneck.
-
- The key question is _"Is it fast enough?"_. Often the answer is yes for typical usage, but developers must be mindful of not doing something on the client that truly requires big iron servers. For instance, full-text indexing of a million documents might be too slow in a client-side DB. **Unpredictable performance** is also a factor: Different users have different devices. A query that takes 50ms on a high-end desktop might take 500ms on a low-end phone in battery saving mode. So performance tuning and testing across devices is needed, and some heavy tasks might still belong on the server side. For example if you build a [local vector database](./javascript-vector-database.md) you might want to create the embeddings on the server and sync them instead of creating them on the client.
-
-- **Client Database Migrations:** As your app evolves, you'll change data models or add new fields. In a cloud-first app, you'd typically run a migration on the server database. In a local-first app, you have not only the server DB (if any) but also every client's local database to consider. Upgrading the schema means you need to write migration logic that runs on each client, perhaps the next time they launch the app after an update. Clients may be offline or not upgrade the app immediately, so you could have different versions of the schema in the wild. This complicates data handling (the sync protocol might need to handle multiple schema versions until everyone is updated). Providing a smooth migration path for local data is doable (many libraries provide [migration facilities](../migration-schema.md)), but it requires careful testing. In a worst case, a failed migration on a client could brick the app for that user or force a full resync. This is a **much bigger headache** than just migrating a centralized DB at midnight while your service is in maintenance mode. 🌃
-
-- **Security and Access Control:** In cloud-based apps, enforcing data security (who can see what) is done on the server. The client only gets the data it's authorized to get. In a local-first scenario, you often need to **partition data per user** on the backend as well, to ensure users only sync down their own data (or data they have permission for). One simple strategy is to give each user their own database or dataset on the server and only replicate that. For example, CouchDB allows creating one database per user and replication can be scoped to that DB which makes permission handling easy. But if you ever need to query across users (say an admin view or aggregate analytics), having data split into many small DBs becomes a pain. The alternative is a single backend database with a **fine-grained access control**, and the client asks to sync only certain documents/fields. That usually means writing a custom sync server or using something like GraphQL with resolvers that respect permissions. In short, **implementing auth and permissions in sync** adds complexity. Also, any data stored on the client is theoretically vulnerable to extraction (if someone compromises the device or uses dev tools). You can [encrypt local databases](../encryption.md) to prevent extraction after the server "revokes" the decryption password to mitigate the data extraction risk.
-
-
-- **Relational Data and Complex Queries:** Most client-side/offline databases are [NoSQL/document oriented](../why-nosql.md) for flexibility in syncing and easy conflict handling. They may not support complex join queries or ACID transactions across multiple tables like a full SQL database would. This is partly because replicating a full relational model is much harder (maintaining referential integrity, etc., when data is partial on a client) or not even logically possible. For example if you have two offline clients running a complex `UPDATE X WHERE Y FROM Z INNER JOIN Alice INNER JOIN Bob` query and then they go online, you have no easy way of handling these conflicts.
-
- If your app has heavy relational data requirements or relies on complex server-side queries (aggregations, multi-join reports), you might find the local database either cannot do it or is too slow to do it client-side. The lack of robust relational querying is something to plan for and you might need to adjust your data model to be more document-oriented or use client-side libraries to run [joins in memory](../why-nosql.md#relational-queries-in-nosql). Most tools use NoSQL because it makes replication easy and implementing true relational sync would require extremely sophisticated solutions and even needing an atomic clock for full consistency across nodes (like google spanner). So, **if your app truly needs SQL power on the client**, local-first might complicate things.
-
- > In Local-First, most tools use NoSQL because it makes replication and conflict handling easy.
-
-That's a long list of challenges! In summary, local-first approaches introduce distributed data issues on the client side that web developers usually didn't have to deal with. Despite these challenges, the local-first movement is steadily growing because the **benefits to user experience and data control are very compelling** and modern tools are emerging to mitigate a lot of these difficulties. All of these are solved or solvable for your specific use-case, just keep them in mind before you start architecting your local-first app.
-
-## Local-First vs. Traditional Online-First Approaches
-
-So now that you know the pros and cons about Local-First. Lets directly compare it to your previous "online-first" stack:
-
-### Connectivity and Offline Usage
-- **Local-first**: Apps like WhatsApp store messages locally on your device, letting you read past messages and even compose new ones without a stable network connection. Once online, the messages sync with the server and other participants.
-- **Online-first**: Purely cloud-based apps typically stop functioning (beyond simple caching) when the network or server goes down. They rely on a constant connection to fetch or store user data.
-
-### Latency and Performance
-- **Local-first**: Because data operations happen on the device, you see near-instant interactions (e.g., typing and reading chats feels very responsive, as the messages are on your phone). Syncing occurs in the background without delaying the user.
-- **Online-first**: Most interactions involve a round-trip to the server, adding network latency and potentially requiring loading states or fallback UIs. If the network is slow or unreliable, users can experience delays when sending or receiving updates.
-
-### Complexity and Conflict Resolution
-- **Local-first**: Much of the complexity like storing data or handling conflicts, shifts to the client. WhatsApp, for instance, caches all messages locally and queues unsent messages offline. Once reconnected, it must reconcile with the server and other devices, especially if the same account is used on multiple platforms.
-- **Online-first**: A single central server manages data and concurrency, so clients remain thinner. However, the app becomes unusable if connectivity is lost, and any server downtime directly impacts all users.
-
-### Data Ownership and Storage Limits
-- **Local-first**: Users hold a complete copy of data on their own device, retaining control and quick access. This can raise challenges when data sets are very large; phone storage might be insufficient, or backups may be harder to guarantee.
-- **Online-first**: Storing all data in the cloud scales more easily, and providers often manage backups automatically. On the downside, users depend on the service and must trust it to protect their data.
-
-### When to Choose Which
-- **Local-first**: Particularly helpful for scenarios where offline operation and immediate responsiveness matter, such as chat apps (like WhatsApp), field tools in low-connectivity environments, or any use case where users can't rely on being online 24/7.
-- **Online-first**: Well-suited for systems that demand real-time central control or massive data aggregation with minimal offline needs, such as large-scale analytics platforms or services where guaranteed immediate global consistency is essential.
-- **Hybrid**: In reality, most modern apps often blend these approaches, giving users partial offline capabilities (caching, queued updates) alongside a robust central service. This hybrid method offers the benefits of local speed and resilience, while still leveraging cloud infrastructure for collaboration and global reach. But a truth local-first app is way more than just a cache.
-
----
-
-
-
-## Offline-First vs. Local-First
-
-In the early days of offline-capable web apps (around 2014), the common phrase was **"Offline-First"**. Tools like **PouchDB** popularized the notion that developers should assume devices are often offline or have flaky connections, so apps must continue to work seamlessly without a network. The guiding principle was *"apps should treat being online as optional."* If a user has no internet access, the application's core features still function, saving or queuing data locally, and automatically synchronizing once connectivity is restored.
-
-
-
-
-
-Over time, this focus on offline support evolved into the broader concept of **"Local-First Software,"** (see [Ink&Switch](https://martin.kleppmann.com/papers/local-first.pdf)) emphasizing not just offline operation but also the technical underpinnings of **storing data locally** in the client application. While offline-first is primarily about resilience to network loss, local-first highlights ownership, privacy, and performance benefits of keeping the primary data on the user's device. Most tools these days extended the original offline-first concepts, adding real-time reactivity, custom sync, and more nuances like conflict resolution or encryption.
-
-
-However, the term **"local-first"** can be **confusing** to non-technical audiences because many people (especially in the US) associate "local first" with *community-oriented movements* that encourage buying from nearby businesses or supporting local initiatives. To reduce ambiguity, it may be clearer to use **"local first software"** or **"local first development"** in your documentation and marketing materials. When creating branding or logos around local-first software, **avoid using the "Google Maps Pin"** as a symbol. This icon typically implies geolocation or physical locality further mixing up the notion of "location-based services" with "on-device data storage."
-
-## Do People Actually Use Local-First or Is It Just a Trend?
-
-If we look at **npm download statistics**, we see that **PouchDB** - one of the oldest libraries for local-first apps - has about **53k** downloads each week, and **RxDB** - a newer library - has about **22k** weekly downloads. Other local-first tools often have even fewer downloads. In comparison, a popular library like **react-query**, which does not focus on local storage, is downloaded about **1.6 million** times a week. These numbers show that local-first libraries, while used, are not as common as some of the more traditional tools.
-
-One reason is that **local-first** is still a new idea. Many developers are used to traditional "online-first" approaches, so switching to a local database and then syncing changes later can feel unfamiliar. Developers must learn different patterns, deal with offline synchronization, and handle possible conflicts. That extra work can be a barrier to adoption which might change in the future as tooling improves.
-
-While most of RxDB is open source, there are also **premium plugins** that help sustain RxDB as a long-term project. Because people purchase these plugins, we gains insights into how developers are using local-first features:
-- About **half** of these users mainly want **offline functionality** for cases such as farming equipment, mining, construction, or even a shrimp farm app.
-- The **other half** focus on **faster, real-time UIs** for to-do or reading apps, a space launch planning tool, and various dashboard apps. This range of use cases highlights both the resilience offline mode can offer and the performance boost that local databases can provide when synced in the background.
-
-## Why Local-First Is the Future
-
-Early in the history of the web, users **expected** static pages. If you wanted to see new content, you **reloaded** the page. That was normal at the time, and nobody found it strange because everything worked that way.
-
-Then, as more sites added **real-time** features - auto-updating feeds, live notifications, single-page apps - suddenly those older "reload-only" sites began to feel **slow** or **outdated**. Why wait for a manual refresh when real-time data was possible and readily available?
-
-The same pattern is happening with **local-first** apps. Right now, most sites are still built around network availability. We see loading spinners whenever data is fetched, and we simply wait for the server response. As local-first experiences become **commonplace** - removing spinners, letting users keep working when offline, and syncing in the background - everything else will start to feel **frustratingly behind**. Users won't tolerate slow or blocked interactions if they've seen apps that respond instantly and remain usable offline. They'll expect that as the default and we'll likely see growing pressure on developers to eliminate those extra loading steps. For many users, the experience of **immediate local writes will become not just a perk, but an expectation!**
-
-## See also
-
-
-
-
-
-
-
-- Discuss [this topic on HackerNews](https://news.ycombinator.com/item?id=43289885)
-- [Local-First Technologies](../alternatives.md): A list of databases and technologies (besides [RxDB](/)) that support offline-first or local-first use cases.
-- [Discord](/chat/): Join our Discord server to talk with people and share ideas about this topic.
-- [Inc&Switch](https://martin.kleppmann.com/papers/local-first.pdf): The "original" paper about Local-First from 2019 where the naming of local-first Software was first used and described.
-- [Learn how to build a local-first Application with RxDB](../quickstart.md).
diff --git a/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html b/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html
deleted file mode 100644
index 293809cd3c3..00000000000
--- a/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html
+++ /dev/null
@@ -1,241 +0,0 @@
-
-
-
-
-
-LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite
-
So you are building that web application and you want to store data inside of your users browser. Maybe you just need to store some small flags or you even need a fully fledged database.
-
The types of web applications we build have changed significantly. In the early years of the web we served static html files. Then we served dynamically rendered html and later we build single page applications that run most logic on the client. And for the coming years you might want to build so called local first apps that handle big and complex data operations solely on the client and even work when offline, which gives you the opportunity to build zero-latency user interactions.
-
In the early days of the web, cookies were the only option for storing small key-value assignments.. But JavaScript and browsers have evolved significantly and better storage APIs have been added which pave the way for bigger and more complex data operations.
-
In this article, we will dive into the various technologies available for storing and querying data in a browser. We'll explore traditional methods like Cookies, localStorage, WebSQL, IndexedDB and newer solutions such as OPFS and SQLite via WebAssembly. We compare the features and limitations and through performance tests we aim to uncover how fast we can write and read data in a web application with the various methods.
-
note
You are reading this in the RxDB docs. RxDB is a JavaScript database that has different storage adapters which can utilize the different storage APIs.
-Since 2017 I spend most of my time working with these APIs, doing performance tests and building hacks and plugins to reach the limits of browser database operation speed.
Cookies were first introduced by netscape in 1994.
-Cookies store small pieces of key-value data that are mainly used for session management, personalization, and tracking. Cookies can have several security settings like a time-to-live or the domain attribute to share the cookies between several subdomains.
-
Cookies values are not only stored at the client but also sent with every http request to the server. This means we cannot store much data in a cookie but it is still interesting how good cookie access performance compared to the other methods. Especially because cookies are such an important base feature of the web, many performance optimizations have been done and even these days there is still progress being made like the Shared Memory Versioning by chromium or the asynchronous CookieStore API.
The localStorage API was first proposed as part of the WebStorage specification in 2009.
-LocalStorage provides a simple API to store key-value pairs inside of a web browser. It has the methods setItem, getItem, removeItem and clear which is all you need from a key-value store. LocalStorage is only suitable for storing small amounts of data that need to persist across sessions and it is limited by a 5MB storage cap. Storing complex data is only possible by transforming it into a string for example with JSON.stringify().
-The API is not asynchronous which means if fully blocks your JavaScript process while doing stuff. Therefore running heavy operations on it might block your UI from rendering.
-
-
There is also the SessionStorage API. The key difference is that localStorage data persists indefinitely until explicitly cleared, while sessionStorage data is cleared when the browser tab or window is closed.
IndexedDB was first introduced as "Indexed Database API" in 2015.
-
IndexedDB is a low-level API for storing large amounts of structured JSON data. While the API is a bit hard to use, IndexedDB can utilize indexes and asynchronous operations. It lacks support for complex queries and only allows to iterate over the indexes which makes it more like a base layer for other libraries then a fully fledged database.
-
In 2018, IndexedDB version 2.0 was introduced. This added some major improvements. Most noticeable the getAll() method which improves performance dramatically when fetching bulks of JSON documents.
-
IndexedDB version 3.0 is in the workings which contains many improvements. Most important the addition of Promise based calls that makes modern JS features like async/await more useful.
The Origin Private File System (OPFS) is a relatively new API that allows web applications to store large files directly in the browser. It is designed for data-intensive applications that want to write and read binary data in a simulated file system.
Or in a WebWorker with the faster, asynchronous access with the createSyncAccessHandle() method.
-
-
Because only binary data can be processed, OPFS is made to be a base filesystem for library developers. You will unlikely directly want to use the OPFS in your code when you build a "normal" application because it is too complex. That would only make sense for storing plain files like images, not to store and query JSON data efficiently. I have build a OPFS based storage for RxDB with proper indexing and querying and it took me several months.
WebAssembly (Wasm) is a binary format that allows high-performance code execution on the web.
-Wasm was added to major browsers over the course of 2017 which opened a wide range of opportunities on what to run inside of a browser. You can compile native libraries to WebAssembly and just run them on the client with just a few adjustments. WASM code can be shipped to browser apps and generally runs much faster compared to JavaScript, but still about 10% slower then native.
-
Many people started to use compiled SQLite as a database inside of the browser which is why it makes sense to also compare this setup to the native APIs.
-
The compiled byte code of SQLite has a size of about 938.9 kB which must be downloaded and parsed by the users on the first page load. WASM cannot directly access any persistent storage API in the browser. Instead it requires data to flow from WASM to the main-thread and then can be put into one of the browser APIs. This is done with so called VFS (virtual file system) adapters that handle data access from SQLite to anything else.
WebSQL was a web API introduced in 2009 that allowed browsers to use SQL databases for client-side storage, based on SQLite. The idea was to give developers a way to store and query data using SQL on the client side, similar to server-side databases.
-WebSQL has been removed from browsers in the current years for multiple good reasons:
-
-
WebSQL was not standardized and having an API based on a single specific implementation in form of the SQLite source code is hard to ever make it to a standard.
-
WebSQL required browsers to use a specific version of SQLite (version 3.6.19) which means whenever there would be any update or bugfix to SQLite, it would not be possible to add that to WebSQL without possible breaking the web.
-
Major browsers like firefox never supported WebSQL.
-
-
Therefore in the following we will just ignore WebSQL even if it would be possible to run tests on in by setting specific browser flags or using old versions of chromium.
Now that you know the basic concepts of the APIs, lets compare some specific features that have shown to be important for people using RxDB and browser based storages in general.
When you store data in a web application, most often you want to store complex JSON documents and not only "normal" values like the integers and strings you store in a server side database.
-
-
Only IndexedDB works with JSON objects natively.
-
With SQLite WASM you can store JSON in a text column since version 3.38.0 (2022-02-22) and even run deep queries on it and use single attributes as indexes.
-
-
Every of the other APIs can only store strings or binary data. Of course you can transform any JSON object to a string with JSON.stringify() but not having the JSON support in the API can make things complex when running queries and running JSON.stringify() many times can cause performance problems.
A big difference when building a Web App compared to Electron or React-Native, is that the user will open and close the app in multiple browser tabs at the same time. Therefore you have not only one JavaScript process running, but many of them can exist and might have to share state changes between each other to not show outdated data to the user.
-
-
If your users' muscle memory puts the left hand on the F5 key while using your website, you did something wrong!
-
-
Not all storage APIs support a way to automatically share write events between tabs.
-
Only localstorage has a way to automatically share write events between tabs by the API itself with the storage-event which can be used to observe changes.
-
// localStorage can observe changes with the storage event.
-// This feature is missing in IndexedDB and others
-addEventListener("storage", (event) => {});
To workaround this problem, there are two solutions:
-
-
The first option is to use the BroadcastChannel API which can send messages across browser tabs. So whenever you do a write to the storage, you also send a notification to other tabs to inform them about these changes. This is the most common workaround which is also used by RxDB. Notice that there is also the WebLocks API which can be used to have mutexes across browser tabs.
-
The other solution is to use the SharedWorker and do all writes inside of the worker. All browser tabs can then subscribe to messages from that single SharedWorker and know about changes.
The big difference between a database and storing data in a plain file, is that a database is writing data in a format that allows running operations over indexes to facilitate fast performant queries. From our list of technologies only IndexedDB and WASM SQLite support for indexing out of the box. In theory you can build indexes on top of any storage like localstorage or OPFS but you likely should not want to do that by yourself.
-
In IndexedDB for example, we can fetch a bulk of documents by a given index range:
-
// find all products with a price between 10 and 50
-const keyRange = IDBKeyRange.bound(10, 50);
-const transaction = db.transaction('products', 'readonly');
-const objectStore = transaction.objectStore('products');
-const index = objectStore.index('priceIndex');
-const request = index.getAll(keyRange);
-const result = await new Promise((res, rej) => {
- request.onsuccess = (event) => res(event.target.result);
- request.onerror = (event) => rej(event);
-});
-
Notice that IndexedDB has the limitation of not having indexes on boolean values. You can only index strings and numbers. To workaround that you have to transform boolean to numbers and backwards when storing the data.
When running heavy data operations, you might want to move the processing away from the JavaScript main thread. This ensures that our app keeps being responsive and fast while the processing can run in parallel in the background. In a browser you can either use the WebWorker, SharedWorker or the ServiceWorker API to do that. In RxDB you can use the WebWorker or SharedWorker plugins to move your storage inside of a worker.
-
The most common API for that use case is spawning a WebWorker and doing most work on that second JavaScript process. The worker is spawned from a separate JavaScript file (or base64 string) and communicates with the main thread by sending data with postMessage().
-
Unfortunately LocalStorage and Cookiescannot be used in WebWorker or SharedWorker because of the design and security constraints. WebWorkers run in a separate global context from the main browser thread and therefore cannot do stuff that might impact the main thread. They have no direct access to certain web APIs, like the DOM, localStorage, or cookies.
-
Everything else can be used from inside a WebWorker.
-The fast version of OPFS with the createSyncAccessHandle method can onlybe used in a WebWorker, and not on the main thread. This is because all the operations of the returned AccessHandle are not async and therefore block the JavaScript process, so you do want to do that on the main thread and block everything.
Cookies are limited to about 4 KB of data in RFC-6265. Because the stored cookies are send to the server with every HTTP request, this limitation is reasonable. You can test your browsers cookie limits here. Notice that you should never fill up the full 4 KB of your cookies because your web server will not accept too long headers and reject the requests with HTTP ERROR 431 - Request header fields too large. Once you have reached that point you can not even serve updated JavaScript to your user to clean up the cookies and you will have locked out that user until the cookies get cleaned up manually.
-
-
-
LocalStorage has a storage size limitation that varies depending on the browser, but generally ranges from 4 MB to 10 MB per origin. You can test your localStorage size limit here.
-
-
Chrome/Chromium/Edge: 5 MB per domain
-
Firefox: 10 MB per domain
-
Safari: 4-5 MB per domain (varies slightly between versions)
-
-
-
-
IndexedDB does not have a specific fixed size limitation like localStorage. The maximum storage size for IndexedDB depends on the browser implementation. The upper limit is typically based on the available disc space on the user's device. In chromium browsers it can use up to 80% of total disk space. You can get an estimation about the storage size limit by calling await navigator.storage.estimate(). Typically you can store gigabytes of data which can be tried out here. Notice that we have a full article about storage max size limits of IndexedDB that covers this topic.
-
-
-
OPFS has the same storage size limitation as IndexedDB. Its limit depends on the available disc space. This can also be tested here.
Now that we've reviewed the features of each storage method, let's dive into performance comparisons, focusing on initialization times, read/write latencies, and bulk operations.
-
Notice that we only run simple tests and for your specific use case in your application the results might differ. Also we only compare performance in google chrome (version 128.0.6613.137). Firefox and Safari have similar but not equal performance patterns. You can run the test by yourself on your own machine from this github repository. For all tests we throttle the network to behave like the average german internet speed. (download: 135,900 kbit/s, upload: 28,400 kbit/s, latency: 125ms). Also all tests store an "average" JSON object that might be required to be stringified depending on the storage. We also only test the performance of storing documents by id because some of the technologies (cookies, OPFS and localstorage) do not support indexed range operations so it makes no sense to compare the performance of these.
Before you can store any data, many APIs require a setup process like creating databases, spawning WebAssembly processes or downloading additional stuff. To ensure your app starts fast, the initialization time is important.
-
The APIs of localStorage and Cookies do not have any setup process and can be directly used. IndexedDB requires to open a database and a store inside of it. WASM SQLite needs to download a WASM file and process it. OPFS needs to download and start a worker file and initialize the virtual file system directory.
-
Here are the time measurements from how long it takes until the first bit of data can be stored:
-
Technology
Time in Milliseconds
IndexedDB
46
OPFS Main Thread
23
OPFS WebWorker
26.8
WASM SQLite (memory)
504
WASM SQLite (IndexedDB)
535
-
Here we can notice a few things:
-
-
Opening a new IndexedDB database with a single store takes surprisingly long
-
The latency overhead of sending data from the main thread to a WebWorker OPFS is about 4 milliseconds. Here we only send minimal data to init the OPFS file handler. It will be interesting if that latency increases when more data is processed.
-
Downloading and parsing WASM SQLite and creating a single table takes about half a second. Using also the IndexedDB VFS to store data persistently adds additional 31 milliseconds. Reloading the page with enabled caching and already prepared tables is a bit faster with 420 milliseconds (memory).
Next lets test the latency of small writes. This is important when you do many small data changes that happen independent from each other. Like when you stream data from a websocket or persist pseudo randomly happening events like mouse movements.
-
Technology
Time in Milliseconds
Cookies
0.058
LocalStorage
0.017
IndexedDB
0.17
OPFS Main Thread
1.46
OPFS WebWorker
1.54
WASM SQLite (memory)
0.17
WASM SQLite (IndexedDB)
3.17
-
Here we can notice a few things:
-
-
LocalStorage has the lowest write latency with only 0.017 milliseconds per write.
-
IndexedDB writes are about 10 times slower compared to localStorage.
-
Sending the data to the WASM SQLite process and letting it persist via IndexedDB is slow with over 3 milliseconds per write.
-
-
The OPFS operations take about 1.5 milliseconds to write the JSON data into one document per file. We can see the sending the data to a webworker first is a bit slower which comes from the overhead of serializing and deserializing the data on both sides.
-If we would not create on OPFS file per document but instead append everything to a single file, the performance pattern changes significantly. Then the faster file handle from the createSyncAccessHandle() only takes about 1 millisecond per write. But this would require to somehow remember at which position the each document is stored. Therefore in our tests we will continue using one file per document.
As next step, lets do some big bulk operations with 200 documents at once.
-
Technology
Time in Milliseconds
Cookies
20.6
LocalStorage
5.79
IndexedDB
13.41
OPFS Main Thread
280
OPFS WebWorker
104
WASM SQLite (memory)
19.1
WASM SQLite (IndexedDB)
37.12
-
Here we can notice a few things:
-
-
Sending the data to a WebWorker and running it via the faster OPFS API is about twice as fast.
-
WASM SQLite performs better on bulk operations compared to its single write latency. This is because sending the data to WASM and backwards is faster if it is done all at once instead of once per document.
Reading many files in the OPFS webworker is about twice as fast compared to the slower main thread mode.
-
WASM SQLite is surprisingly fast. Further inspection has shown that the WASM SQLite process keeps the documents in memory cached which improves the latency when we do reads directly after writes on the same data. When the browser tab is reloaded between the writes and the reads, finding the 100 documents takes about 35 milliseconds instead.
LocalStorage is really fast but remember that is has some downsides:
-
-
It blocks the main JavaScript process and therefore should not be used for big bulk operations.
-
Only Key-Value assignments are possible, you cannot use it efficiently when you need to do index based range queries on your data.
-
-
-
OPFS is way faster when used in the WebWorker with the createSyncAccessHandle() method compare to using it directly in the main thread.
-
SQLite WASM can be fast but you have to initially download the full binary and start it up which takes about half a second. This might not be relevant at all if your app is started up once and the used for a very long time. But for web-apps that are opened and closed in many browser tabs many times, this might be a problem.
There is a wide range of possible improvements and performance hacks to speed up the operations.
-
-
For IndexedDB I have made a list of performance hacks here. For example you can do sharding between multiple database and webworkers or use a custom index strategy.
-
OPFS is slow in writing one file per document. But you do not have to do that and instead you can store everything at a single file like a normal database would do. This improves performance dramatically like it was done with the RxDB OPFS RxStorage.
-
You can mix up the technologies to optimize for multiple scenarios at once. For example in RxDB there is the localstorage meta optimizer which stores initial metadata in localstorage and "normal" documents inside of IndexedDB. This improves the initial startup time while still having the documents stored in a way to query them efficiently.
-
There is the memory-mapped storage plugin in RxDB which maps data directly to memory. Using this in combination with a shared worker can improve pageloads and query time significantly.
-
Compressing data before storing it might improve the performance for some of the storages.
-
Splitting work up between multiple WebWorkers via sharding can improve performance by utilizing the whole capacity of your users device.
-
-
Here you can see the performance comparison of various RxDB storage implementations which gives a better view of real world performance:
You are reading this in 2024, but the web does not stand still. There is a good chance that browser get enhanced to allow faster and better data operations.
-
-
Currently there is no way to directly access a persistent storage from inside a WebAssembly process. If this changes in the future, running SQLite (or a similar database) in a browser might be the best option.
-
Sending data between the main thread and a WebWorker is slow but might be improved in the future. There is a good article about why postMessage() is slow.
-
IndexedDB lately got support for storage buckets (chrome only) which might improve performance.
-
-
\ No newline at end of file
diff --git a/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.md b/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.md
deleted file mode 100644
index e5a762ebcce..00000000000
--- a/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.md
+++ /dev/null
@@ -1,341 +0,0 @@
-# LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite
-
-> Compare LocalStorage, IndexedDB, Cookies, OPFS, and WASM-SQLite for web storage, performance, limits, and best practices for modern web apps.
-
-
-
-# LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite
-
-So you are building that web application and you want to **store data inside of your users browser**. Maybe you just need to store some small flags or you even need a fully fledged database.
-
-The types of web applications we build have changed significantly. In the early years of the web we served static html files. Then we served dynamically rendered html and later we build **single page applications** that run most logic on the client. And for the coming years you might want to build so called [local first apps](../offline-first.md) that handle big and complex data operations solely on the client and even work when offline, which gives you the opportunity to build **zero-latency** user interactions.
-
-In the early days of the web, **cookies** were the only option for storing small key-value assignments.. But JavaScript and browsers have evolved significantly and better storage APIs have been added which pave the way for bigger and more complex data operations.
-
-In this article, we will dive into the various technologies available for storing and querying data in a browser. We'll explore traditional methods like **Cookies**, **localStorage**, **WebSQL**, **IndexedDB** and newer solutions such as **OPFS** and **SQLite via WebAssembly**. We compare the features and limitations and through performance tests we aim to uncover how fast we can write and read data in a web application with the various methods.
-
-:::note
-You are reading this in the [RxDB](/) docs. RxDB is a JavaScript database that has different storage adapters which can utilize the different storage APIs.
-**Since 2017** I spend most of my time working with these APIs, doing performance tests and building [hacks](../slow-indexeddb.md) and plugins to reach the limits of browser database operation speed.
-
-
-
-
-
-
-:::
-
-## The available Storage APIs in a modern Browser
-
-First lets have a brief overview of the different APIs, their intentional use case and history:
-
-### What are Cookies
-
-Cookies were first introduced by [netscape in 1994](https://www.baekdal.com/thoughts/the-original-cookie-specification-from-1997-was-gdpr-compliant/).
-Cookies store small pieces of key-value data that are mainly used for session management, personalization, and tracking. Cookies can have several security settings like a time-to-live or the `domain` attribute to share the cookies between several subdomains.
-
-Cookies values are not only stored at the client but also sent with **every http request** to the server. This means we cannot store much data in a cookie but it is still interesting how good cookie access performance compared to the other methods. Especially because cookies are such an important base feature of the web, many performance optimizations have been done and even these days there is still progress being made like the [Shared Memory Versioning](https://blog.chromium.org/2024/06/introducing-shared-memory-versioning-to.html) by chromium or the asynchronous [CookieStore API](https://developer.mozilla.org/en-US/docs/Web/API/Cookie_Store_API).
-
-### What is LocalStorage
-
-The [localStorage API](./localstorage.md) was first proposed as part of the [WebStorage specification in 2009](https://www.w3.org/TR/2009/WD-webstorage-20090423/#the-localstorage-attribute).
-LocalStorage provides a simple API to store key-value pairs inside of a web browser. It has the methods `setItem`, `getItem`, `removeItem` and `clear` which is all you need from a key-value store. LocalStorage is only suitable for storing small amounts of data that need to persist across sessions and it is [limited by a 5MB storage cap](./localstorage.md#understanding-the-limitations-of-local-storage). Storing complex data is only possible by transforming it into a string for example with `JSON.stringify()`.
-The API is not asynchronous which means if fully blocks your JavaScript process while doing stuff. Therefore running heavy operations on it might block your UI from rendering.
-
-> There is also the **SessionStorage** API. The key difference is that localStorage data persists indefinitely until explicitly cleared, while sessionStorage data is cleared when the browser tab or window is closed.
-
-### What is IndexedDB
-
-IndexedDB was first introduced as "Indexed Database API" [in 2015](https://www.w3.org/TR/IndexedDB/#sotd).
-
-[IndexedDB](../rx-storage-indexeddb.md) is a low-level API for storing large amounts of structured JSON data. While the API is a bit hard to use, IndexedDB can utilize indexes and asynchronous operations. It lacks support for complex queries and only allows to iterate over the indexes which makes it more like a base layer for other libraries then a fully fledged database.
-
-In 2018, IndexedDB version 2.0 [was introduced](https://hacks.mozilla.org/2016/10/whats-new-in-indexeddb-2-0/). This added some major improvements. Most noticeable the `getAll()` method which improves performance dramatically when fetching bulks of JSON documents.
-
-IndexedDB [version 3.0](https://w3c.github.io/IndexedDB/) is in the workings which contains many improvements. Most important the addition of `Promise` based calls that makes modern JS features like `async/await` more useful.
-
-### What is OPFS
-
-The [Origin Private File System](../rx-storage-opfs.md) (OPFS) is a [relatively new](https://caniuse.com/mdn-api_filesystemfilehandle_createsyncaccesshandle) API that allows web applications to store large files directly in the browser. It is designed for data-intensive applications that want to write and read **binary data** in a simulated file system.
-
-OPFS can be used in two modes:
-- Either asynchronous on the [main thread](../rx-storage-opfs.md#using-opfs-in-the-main-thread-instead-of-a-worker)
-- Or in a WebWorker with the faster, asynchronous access with the `createSyncAccessHandle()` method.
-
-Because only binary data can be processed, OPFS is made to be a base filesystem for library developers. You will unlikely directly want to use the OPFS in your code when you build a "normal" application because it is too complex. That would only make sense for storing plain files like images, not to store and query [JSON data](./json-based-database.md) efficiently. I have build a [OPFS based storage](../rx-storage-opfs.md) for RxDB with proper indexing and querying and it took me several months.
-
-### What is WASM SQLite
-
-
-
-
-
-[WebAssembly](https://webassembly.org/) (Wasm) is a binary format that allows high-performance code execution on the web.
-Wasm was added to major browsers over the course of 2017 which opened a wide range of opportunities on what to run inside of a browser. You can compile native libraries to WebAssembly and just run them on the client with just a few adjustments. WASM code can be shipped to browser apps and generally runs much faster compared to JavaScript, but still about [10% slower then native](https://www.usenix.org/conference/atc19/presentation/jangda).
-
-Many people started to use compiled SQLite as a database inside of the browser which is why it makes sense to also compare this setup to the native APIs.
-
-The compiled byte code of SQLite has a size of [about 938.9 kB](https://sqlite.org/download.html) which must be downloaded and parsed by the users on the first page load. WASM cannot directly access any persistent storage API in the browser. Instead it requires data to flow from WASM to the main-thread and then can be put into one of the browser APIs. This is done with so called [VFS (virtual file system) adapters](https://www.sqlite.org/vfs.html) that handle data access from SQLite to anything else.
-
-### What was WebSQL
-
-WebSQL **was** a web API [introduced in 2009](https://www.w3.org/TR/webdatabase/) that allowed browsers to use SQL databases for client-side storage, based on SQLite. The idea was to give developers a way to store and query data using SQL on the client side, similar to server-side databases.
-WebSQL has been **removed from browsers** in the current years for multiple good reasons:
-
-- WebSQL was not standardized and having an API based on a single specific implementation in form of the SQLite source code is hard to ever make it to a standard.
-- WebSQL required browsers to use a [specific version](https://developer.chrome.com/blog/deprecating-web-sql#reasons_for_deprecating_web_sql) of SQLite (version 3.6.19) which means whenever there would be any update or bugfix to SQLite, it would not be possible to add that to WebSQL without possible breaking the web.
-- Major browsers like firefox never supported WebSQL.
-
-Therefore in the following we will **just ignore WebSQL** even if it would be possible to run tests on in by setting specific browser flags or using old versions of chromium.
-
--------------
-
-## Feature Comparison
-
-Now that you know the basic concepts of the APIs, lets compare some specific features that have shown to be important for people using RxDB and browser based storages in general.
-
-### Storing complex JSON Documents
-
-When you store data in a web application, most often you want to store complex JSON documents and not only "normal" values like the `integers` and `strings` you store in a server side database.
-
-- Only IndexedDB works with JSON objects natively.
-- With SQLite WASM you can [store JSON](https://www.sqlite.org/json1.html) in a `text` column since version 3.38.0 (2022-02-22) and even run deep queries on it and use single attributes as indexes.
-
-Every of the other APIs can only store strings or binary data. Of course you can transform any JSON object to a string with `JSON.stringify()` but not having the JSON support in the API can make things complex when running queries and running `JSON.stringify()` many times can cause performance problems.
-
-### Multi-Tab Support
-
-A big difference when building a Web App compared to [Electron](../electron-database.md) or [React-Native](../react-native-database.md), is that the user will open and close the app in **multiple browser tabs at the same time**. Therefore you have not only one JavaScript process running, but many of them can exist and might have to share state changes between each other to not show **outdated data** to the user.
-
-> If your users' muscle memory puts the left hand on the **F5** key while using your website, you did something wrong!
-
-Not all storage APIs support a way to automatically share write events between tabs.
-
-Only localstorage has a way to automatically share write events between tabs by the API itself with the [storage-event](./localstorage.md#localstorage-vs-indexeddb) which can be used to observe changes.
-
-```js
-// localStorage can observe changes with the storage event.
-// This feature is missing in IndexedDB and others
-addEventListener("storage", (event) => {});
-```
-
-There was the [experimental IndexedDB observers API](https://stackoverflow.com/a/33270440) for chrome, but the proposal repository has been archived.
-
-To workaround this problem, there are two solutions:
-- The first option is to use the [BroadcastChannel API](https://github.com/pubkey/broadcast-channel) which can send messages across browser tabs. So whenever you do a write to the storage, you also send a notification to other tabs to inform them about these changes. This is the most common workaround which is also used by RxDB. Notice that there is also the [WebLocks API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API) which can be used to have mutexes across browser tabs.
-- The other solution is to use the [SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) and do all writes inside of the worker. All browser tabs can then subscribe to messages from that **single** SharedWorker and know about changes.
-
-### Indexing Support
-
-The big difference between a database and storing data in a plain file, is that a database is writing data in a format that allows running operations over indexes to facilitate fast performant queries. From our list of technologies only **IndexedDB** and **WASM SQLite** support for indexing out of the box. In theory you can build indexes on top of any storage like localstorage or OPFS but you likely should not want to do that by yourself.
-
-In IndexedDB for example, we can fetch a bulk of documents by a given index range:
-
-```ts
-// find all products with a price between 10 and 50
-const keyRange = IDBKeyRange.bound(10, 50);
-const transaction = db.transaction('products', 'readonly');
-const objectStore = transaction.objectStore('products');
-const index = objectStore.index('priceIndex');
-const request = index.getAll(keyRange);
-const result = await new Promise((res, rej) => {
- request.onsuccess = (event) => res(event.target.result);
- request.onerror = (event) => rej(event);
-});
-```
-
-Notice that IndexedDB has the limitation of [not having indexes on boolean values](https://github.com/w3c/IndexedDB/issues/76). You can only index strings and numbers. To workaround that you have to transform boolean to numbers and backwards when storing the data.
-
-### WebWorker Support
-
-When running heavy data operations, you might want to move the processing away from the JavaScript main thread. This ensures that our app keeps being responsive and fast while the processing can run in parallel in the background. In a browser you can either use the [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API), [SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) or the [ServiceWorker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) API to do that. In RxDB you can use the [WebWorker](../rx-storage-worker.md) or [SharedWorker](../rx-storage-shared-worker.md) plugins to move your storage inside of a worker.
-
-The most common API for that use case is spawning a **WebWorker** and doing most work on that second JavaScript process. The worker is spawned from a separate JavaScript file (or base64 string) and communicates with the main thread by sending data with `postMessage()`.
-
-Unfortunately **LocalStorage** and **Cookies** [cannot be used in WebWorker or SharedWorker](https://stackoverflow.com/questions/6179159/accessing-localstorage-from-a-webworker) because of the design and security constraints. WebWorkers run in a separate global context from the main browser thread and therefore cannot do stuff that might impact the main thread. They have no direct access to certain web APIs, like the DOM, localStorage, or cookies.
-
-Everything else can be used from inside a WebWorker.
-The fast version of OPFS with the `createSyncAccessHandle` method can **only** [be used in a WebWorker](../rx-storage-opfs.md#opfs-limitations), and **not on the main thread**. This is because all the operations of the returned `AccessHandle` are **not async** and therefore block the JavaScript process, so you do want to do that on the main thread and block everything.
-
--------------
-
-## Storage Size Limits
-
-- **Cookies** are limited to about `4 KB` of data in [RFC-6265](https://datatracker.ietf.org/doc/html/rfc6265#section-6.1). Because the stored cookies are send to the server with every HTTP request, this limitation is reasonable. You can test your browsers cookie limits [here](http://www.ruslog.com/tools/cookies.html). Notice that you should never fill up the full `4 KB` of your cookies because your web server will not accept too long headers and reject the requests with `HTTP ERROR 431 - Request header fields too large`. Once you have reached that point you can not even serve updated JavaScript to your user to clean up the cookies and you will have locked out that user until the cookies get cleaned up manually.
-
-- **LocalStorage** has a storage size limitation that varies depending on the browser, but generally ranges from 4 MB to 10 MB per origin. You can test your localStorage size limit [here](https://arty.name/localstorage.html).
- - Chrome/Chromium/Edge: 5 MB per domain
- - Firefox: 10 MB per domain
- - Safari: 4-5 MB per domain (varies slightly between versions)
-
-- **IndexedDB** does not have a specific fixed size limitation like localStorage. The maximum storage size for IndexedDB depends on the browser implementation. The upper limit is typically based on the available disc space on the user's device. In chromium browsers it can use up to 80% of total disk space. You can get an estimation about the storage size limit by calling `await navigator.storage.estimate()`. Typically you can store gigabytes of data which can be tried out [here](https://demo.agektmr.com/storage/). Notice that we have a full article about [storage max size limits of IndexedDB](./indexeddb-max-storage-limit.md) that covers this topic.
-
-- **OPFS** has the same storage size limitation as IndexedDB. Its limit depends on the available disc space. This can also be tested [here](https://demo.agektmr.com/storage/).
-
--------------
-
-## Performance Comparison
-
-Now that we've reviewed the features of each storage method, let's dive into performance comparisons, focusing on initialization times, read/write latencies, and bulk operations.
-
-Notice that we only run simple tests and for your specific use case in your application the results might differ. Also we only compare performance in google chrome (version 128.0.6613.137). Firefox and Safari have similar **but not equal** performance patterns. You can run the test by yourself on your own machine from this [github repository](https://github.com/pubkey/localstorage-indexeddb-cookies-opfs-sqlite-wasm). For all tests we throttle the network to behave like the average german internet speed. (download: 135,900 kbit/s, upload: 28,400 kbit/s, latency: 125ms). Also all tests store an "average" JSON object that might be required to be stringified depending on the storage. We also only test the performance of storing documents by id because some of the technologies (cookies, OPFS and localstorage) do not support indexed range operations so it makes no sense to compare the performance of these.
-
-### Initialization Time
-
-Before you can store any data, many APIs require a setup process like creating databases, spawning WebAssembly processes or downloading additional stuff. To ensure your app starts fast, the initialization time is important.
-
-The APIs of localStorage and Cookies do not have any setup process and can be directly used. IndexedDB requires to open a database and a store inside of it. WASM SQLite needs to download a WASM file and process it. OPFS needs to download and start a worker file and initialize the virtual file system directory.
-
-Here are the time measurements from how long it takes until the first bit of data can be stored:
-
-| Technology | Time in Milliseconds |
-| ----------------------- | -------------------- |
-| IndexedDB | 46 |
-| OPFS Main Thread | 23 |
-| OPFS WebWorker | 26.8 |
-| WASM SQLite (memory) | 504 |
-| WASM SQLite (IndexedDB) | 535 |
-
-Here we can notice a few things:
-
-- Opening a new IndexedDB database with a single store takes surprisingly long
-- The latency overhead of sending data from the main thread to a WebWorker OPFS is about 4 milliseconds. Here we only send minimal data to init the OPFS file handler. It will be interesting if that latency increases when more data is processed.
-- Downloading and parsing WASM SQLite and creating a single table takes about half a second. Using also the IndexedDB VFS to store data persistently adds additional 31 milliseconds. Reloading the page with enabled caching and already prepared tables is a bit faster with 420 milliseconds (memory).
-
-### Latency of small Writes
-
-Next lets test the latency of small writes. This is important when you do many small data changes that happen independent from each other. Like when you stream data from a websocket or persist pseudo randomly happening events like mouse movements.
-
-| Technology | Time in Milliseconds |
-| ----------------------- | -------------------- |
-| Cookies | 0.058 |
-| LocalStorage | 0.017 |
-| IndexedDB | 0.17 |
-| OPFS Main Thread | 1.46 |
-| OPFS WebWorker | 1.54 |
-| WASM SQLite (memory) | 0.17 |
-| WASM SQLite (IndexedDB) | 3.17 |
-
-Here we can notice a few things:
-
-- LocalStorage has the lowest write latency with only 0.017 milliseconds per write.
-- IndexedDB writes are about 10 times slower compared to localStorage.
-- Sending the data to the WASM SQLite process and letting it persist via IndexedDB is slow with over 3 milliseconds per write.
-
-The OPFS operations take about 1.5 milliseconds to write the JSON data into one document per file. We can see the sending the data to a webworker first is a bit slower which comes from the overhead of serializing and deserializing the data on both sides.
-If we would not create on OPFS file per document but instead append everything to a single file, the performance pattern changes significantly. Then the faster file handle from the `createSyncAccessHandle()` only takes about 1 millisecond per write. But this would require to somehow remember at which position the each document is stored. Therefore in our tests we will continue using one file per document.
-
-### Latency of small Reads
-
-Now that we have stored some documents, lets measure how long it takes to read single documents by their `id`.
-
-| Technology | Time in Milliseconds |
-| ----------------------- | -------------------- |
-| Cookies | 0.132 |
-| LocalStorage | 0.0052 |
-| IndexedDB | 0.1 |
-| OPFS Main Thread | 1.28 |
-| OPFS WebWorker | 1.41 |
-| WASM SQLite (memory) | 0.45 |
-| WASM SQLite (IndexedDB) | 2.93 |
-
-Here we can notice a few things:
-
-- LocalStorage reads are **really really fast** with only 0.0052 milliseconds per read.
-- The other technologies perform reads in a similar speed to their write latency.
-
-### Big Bulk Writes
-
-As next step, lets do some big bulk operations with 200 documents at once.
-
-| Technology | Time in Milliseconds |
-| ----------------------- | -------------------- |
-| Cookies | 20.6 |
-| LocalStorage | 5.79 |
-| IndexedDB | 13.41 |
-| OPFS Main Thread | 280 |
-| OPFS WebWorker | 104 |
-| WASM SQLite (memory) | 19.1 |
-| WASM SQLite (IndexedDB) | 37.12 |
-
-Here we can notice a few things:
-
-- Sending the data to a WebWorker and running it via the faster OPFS API is about twice as fast.
-- WASM SQLite performs better on bulk operations compared to its single write latency. This is because sending the data to WASM and backwards is faster if it is done all at once instead of once per document.
-
-### Big Bulk Reads
-
-Now lets read 100 documents in a bulk request.
-
-| Technology | Time in Milliseconds |
-| ----------------------- | ------------------------------- |
-| Cookies | 6.34 |
-| LocalStorage | 0.39 |
-| IndexedDB | 4.99 |
-| OPFS Main Thread | 54.79 |
-| OPFS WebWorker | 25.61 |
-| WASM SQLite (memory) | 3.59 |
-| WASM SQLite (IndexedDB) | 5.84 (35ms without cache) |
-
-Here we can notice a few things:
-
-- Reading many files in the OPFS webworker is about **twice as fast** compared to the slower main thread mode.
-- WASM SQLite is surprisingly fast. Further inspection has shown that the WASM SQLite process keeps the documents in memory cached which improves the latency when we do reads directly after writes on the same data. When the browser tab is reloaded between the writes and the reads, finding the 100 documents takes about **35 milliseconds** instead.
-
-## Performance Conclusions
-
-- LocalStorage is really fast but remember that is has some downsides:
- - It blocks the main JavaScript process and therefore should not be used for big bulk operations.
- - Only Key-Value assignments are possible, you cannot use it efficiently when you need to do index based range queries on your data.
-- OPFS is way faster when used in the WebWorker with the `createSyncAccessHandle()` method compare to using it directly in the main thread.
-- SQLite WASM can be fast but you have to initially download the full binary and start it up which takes about half a second. This might not be relevant at all if your app is started up once and the used for a very long time. But for web-apps that are opened and closed in many browser tabs many times, this might be a problem.
-
--------------
-
-## Possible Improvements
-
-There is a wide range of possible improvements and performance hacks to speed up the operations.
-- For IndexedDB I have made a list of [performance hacks here](../slow-indexeddb.md). For example you can do sharding between multiple database and webworkers or use a custom index strategy.
-- OPFS is slow in writing one file per document. But you do not have to do that and instead you can store everything at a single file like a normal database would do. This improves performance dramatically like it was done with the RxDB [OPFS RxStorage](../rx-storage-opfs.md).
-- You can mix up the technologies to optimize for multiple scenarios at once. For example in RxDB there is the [localstorage meta optimizer](../rx-storage-localstorage-meta-optimizer.md) which stores initial metadata in localstorage and "normal" documents inside of IndexedDB. This improves the initial startup time while still having the documents stored in a way to query them efficiently.
-- There is the [memory-mapped](../rx-storage-memory-mapped.md) storage plugin in RxDB which maps data directly to memory. Using this in combination with a shared worker can improve pageloads and query time significantly.
-- [Compressing](../key-compression.md) data before storing it might improve the performance for some of the storages.
-- Splitting work up between [multiple WebWorkers](../rx-storage-worker.md) via [sharding](../rx-storage-sharding.md) can improve performance by utilizing the whole capacity of your users device.
-
-Here you can see the [performance comparison](../rx-storage-performance.md) of various RxDB storage implementations which gives a better view of real world performance:
-
-
-
-
-
-## Future Improvements
-
-You are reading this in 2024, but the web does not stand still. There is a good chance that browser get enhanced to allow faster and better data operations.
-
-- Currently there is no way to directly access a persistent storage from inside a WebAssembly process. If this changes in the future, running SQLite (or a similar database) in a browser might be the best option.
-- Sending data between the main thread and a WebWorker is slow but might be improved in the future. There is a [good article](https://surma.dev/things/is-postmessage-slow/) about why `postMessage()` is slow.
-- IndexedDB lately [got support](https://developer.chrome.com/blog/maximum-idb-performance-with-storage-buckets) for storage buckets (chrome only) which might improve performance.
-
-## Follow Up
-
-- Share my [announcement tweet](https://x.com/rxdbjs/status/1846145062847062391) -->
-- Reproduce the benchmarks at the [github repo](https://github.com/pubkey/localstorage-indexeddb-cookies-opfs-sqlite-wasm)
-- Learn how to use RxDB with the [RxDB Quickstart](../quickstart.md)
-- Check out the [RxDB github repo](https://github.com/pubkey/rxdb) and leave a star ⭐
diff --git a/docs/articles/localstorage.html b/docs/articles/localstorage.html
deleted file mode 100644
index a5c4b5c98df..00000000000
--- a/docs/articles/localstorage.html
+++ /dev/null
@@ -1,134 +0,0 @@
-
-
-
-
-
-Using localStorage in Modern Applications - A Comprehensive Guide | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Using localStorage in Modern Applications: A Comprehensive Guide
-
When it comes to client-side storage in web applications, the localStorage API stands out as a simple and widely supported solution. It allows developers to store key-value pairs directly in a user's browser. In this article, we will explore the various aspects of the localStorage API, its advantages, limitations, and alternative storage options available for modern applications.
The localStorage API is a built-in feature of web browsers that enables web developers to store small amounts of data persistently on a user's device. It operates on a simple key-value basis, allowing developers to save strings, numbers, and other simple data types. This data remains available even after the user closes the browser or navigates away from the page. The API provides a convenient way to maintain state and store user preferences without relying on server-side storage.
-
Exploring local storage Methods: A Practical Example
-
Let's dive into some hands-on code examples to better understand how to leverage the power of localStorage. The API offers several methods for interaction, including setItem, getItem, removeItem, and clear. Consider the following code snippet:
-
// Storing data using setItem
-localStorage.setItem('username', 'john_doe');
-
-// Retrieving data using getItem
-const storedUsername = localStorage.getItem('username');
-
-// Removing data using removeItem
-localStorage.removeItem('username');
-
-// Clearing all data
-localStorage.clear();
-
Storing Complex Data in JavaScript with JSON Serialization
-
While js localStorage excels at handling simple key-value pairs, it also supports more intricate data storage through JSON serialization. By utilizing JSON.stringify and JSON.parse, you can store and retrieve structured data like objects and arrays. Here's an example of storing a document:
-
const user = {
- name: 'Alice',
- age: 30,
- email: 'alice@example.com'
-};
-
-// Storing a user object
-localStorage.setItem('user', JSON.stringify(user));
-
-// Retrieving and parsing the user object
-const storedUser = JSON.parse(localStorage.getItem('user'));
Despite its convenience, localStorage does come with a set of limitations that developers should be aware of:
-
-
Non-Async Blocking API: One significant drawback is that js localStorage operates as a non-async blocking API. This means that any operations performed on localStorage can potentially block the main thread, leading to slower application performance and a less responsive user experience.
-
Limited Data Structure: Unlike more advanced databases, localStorage is limited to a simple key-value store. This restriction makes it unsuitable for storing complex data structures or managing relationships between data elements.
-
Stringification Overhead: Storing JSON data in localStorage requires stringifying the data before storage and parsing it when retrieved. This process introduces performance overhead, potentially slowing down operations by up to 10 times.
-
Lack of Indexing: localStorage lacks indexing capabilities, making it challenging to perform efficient searches or iterate over data based on specific criteria. This limitation can hinder applications that rely on complex data retrieval.
-
Tab Blocking: In a multi-tab environment, one tab's localStorage operations can impact the performance of other tabs by monopolizing CPU resources. You can reproduce this behavior by opening this test file in two browser windows and trigger localstorage inserts in one of them. You will observe that the indication spinner will stuck in both windows.
-
Storage Limit: Browsers typically impose a storage limit of around 5 MiB for each origin's localStorage.
Contrary to concerns about performance, the localStorage API in JavaScript is surprisingly fast when compared to alternative storage solutions like IndexedDB or OPFS. It excels in handling small key-value assignments efficiently. Due to its simplicity and direct integration with browsers, accessing and modifying localStorage data incur minimal overhead. For scenarios where quick and straightforward data storage is required, localStorage remains a viable option. For example RxDB uses localStorage in the localStorage meta optimizer to manage simple key values pairs while storing the "normal" documents inside of another storage like IndexedDB.
While localStorage offers convenience, it may not be suitable for every use case. Consider the following situations where alternatives might be more appropriate:
-
-
Data Must Be Queryable: If your application relies heavily on querying data based on specific criteria, localStorage might not provide the necessary querying capabilities. Complex data retrieval might lead to inefficient code and slow performance.
-
Big JSON Documents: Storing large JSON documents in localStorage can consume a significant amount of memory and degrade performance. It's essential to assess the size of the data you intend to store and consider more robust solutions for handling substantial datasets.
-
Many Read/Write Operations: Excessive read and write operations on localStorage can lead to performance bottlenecks. Other storage solutions might offer better performance and scalability for applications that require frequent data manipulation.
-
Lack of Persistence: If your application can function without persistent data across sessions, consider using in-memory data structures like new Map() or new Set(). These options offer speed and efficiency for transient data.
-
-
What to use instead of the localStorage API in JavaScript
While localStorage serves as a reliable storage solution for simpler data needs, it's essential to explore alternatives like IndexedDB when dealing with more complex requirements. IndexedDB is designed to store not only key-value pairs but also JSON documents. Unlike localStorage, which usually has a storage limit of around 5-10MB per domain, IndexedDB can handle significantly larger datasets. IndexDB with its support for indexing facilitates efficient querying, making range queries possible. However, it's worth noting that IndexedDB lacks observability, which is a feature unique to localStorage through the storage event. Also,
-complex queries can pose a challenge with IndexedDB, and while its performance is acceptable, IndexedDB can be too slow for some use cases.
-
// localStorage can observe changes with the storage event.
-// This feature is missing in IndexedDB
-addEventListener("storage", (event) => {});
-
For those looking to harness the full power of IndexedDB with added capabilities, using wrapper libraries like RxDB is recommended. These libraries augment IndexedDB with features such as complex queries and observability, enhancing its usability for modern applications by providing a real database instead of only a key-value store.
-
-
In summary when you compare IndexedDB vs localStorage, IndexedDB will win at any case where much data is handled while localStorage has better performance on small key-value datasets.
Another intriguing option is the OPFS (File System API). This API provides direct access to an origin-based, sandboxed filesystem which is highly optimized for performance and offers in-place write access to its content.
-OPFS offers impressive performance benefits. However, working with the OPFS API can be complex, and it's only accessible within a WebWorker. To simplify its usage and extend its capabilities, consider using a wrapper library like RxDB's OPFS RxStorage, which builds a comprehensive database on top of the OPFS API. This abstraction allows you to harness the power of the OPFS API without the intricacies of direct usage.
Cookies, once a primary method of client-side data storage, have fallen out of favor in modern web development due to their limitations. While they can store data, they are about 100 times slower when compared to the localStorage API. Additionally, cookies are included in the HTTP header, which can impact network performance. As a result, cookies are not recommended for data storage purposes in contemporary web applications.
WebSQL, despite offering a SQL-based interface for client-side data storage, is a deprecated technology and should be avoided. Its API has been phased out of modern browsers, and it lacks the robustness of alternatives like IndexedDB. Moreover, WebSQL tends to be around 10 times slower than IndexedDB, making it a suboptimal choice for applications that demand efficient data manipulation and retrieval.
In scenarios where data persistence beyond a session is unnecessary, developers often turn to sessionStorage. This storage mechanism retains data only for the duration of a tab or browser session. It survives page reloads and restores, providing a handy solution for temporary data needs. However, it's important to note that sessionStorage is limited in scope and may not suit all use cases.
For React Native developers, the AsyncStorage API is the go-to solution, mirroring the behavior of localStorage but with asynchronous support. Since not all JavaScript runtimes support localStorage, AsyncStorage offers a seamless alternative for data persistence in React Native applications.
Because native localStorage is absent in the Node.js JavaScript runtime, you will get the error ReferenceError: localStorage is not defined in Node.js or node based runtimes like Next.js. The node-localstorage npm package bridges the gap. This package replicates the browser's localStorage API within the Node.js environment, ensuring consistent and compatible data storage capabilities.
While browser extensions for chrome and firefox support the localStorage API, it is not recommended to use it in that context to store extension-related data. The browser will clear the data in many scenarios like when the users clear their browsing history.
-
Instead the Extension Storage API should be used for browser extensions.
-In contrast to localStorage, the storage API works async and all operations return a Promise. Also it provides automatic sync to replicate data between all instances of that browser that the user is logged into. The storage API is even able to storage JSON-ifiable objects instead of plain strings.
-
// Using the storage API in chrome
-
-await chrome.storage.local.set({ foobar: {nr: 1} });
-
-const result = await chrome.storage.local.get('foobar');
-console.log(result.foobar); // {nr: 1}
The Deno JavaScript runtime has a working localStorage API so running localStorage.setItem() and the other methods, will just work and the locally stored data is persisted across multiple runs.
-
Bun does not support the localStorage JavaScript API. Trying to use localStorage will error with ReferenceError: Can't find variable: localStorage. To store data locally in Bun, you could use the bun:sqlite module instead or directly use a in-JavaScript database with Bun support like RxDB.
In the world of modern web development, localStorage serves as a valuable tool for lightweight data storage. Its simplicity and speed make it an excellent choice for small key-value assignments. However, as application complexity grows, developers must assess their storage needs carefully. For scenarios that demand advanced querying, complex data structures, or high-volume operations, alternatives like IndexedDB, wrapper libraries with additional features like RxDB, or platform-specific APIs offer more robust solutions. By understanding the strengths and limitations of various storage options, developers can make informed decisions that pave the way for efficient and scalable applications.
-
-
\ No newline at end of file
diff --git a/docs/articles/localstorage.md b/docs/articles/localstorage.md
deleted file mode 100644
index faa3a99ac6f..00000000000
--- a/docs/articles/localstorage.md
+++ /dev/null
@@ -1,153 +0,0 @@
-# Using localStorage in Modern Applications - A Comprehensive Guide
-
-> This guide explores localStorage in JavaScript web apps, detailing its usage, limitations, and alternatives like IndexedDB and AsyncStorage.
-
-# Using localStorage in Modern Applications: A Comprehensive Guide
-
-When it comes to client-side storage in web applications, the localStorage API stands out as a simple and widely supported solution. It allows developers to store key-value pairs directly in a user's browser. In this article, we will explore the various aspects of the localStorage API, its advantages, limitations, and alternative storage options available for modern applications.
-
-
-
-
-
-
-
-## What is the localStorage API?
-
-The localStorage API is a built-in feature of web browsers that enables web developers to store small amounts of data persistently on a user's device. It operates on a simple key-value basis, allowing developers to save strings, numbers, and other simple data types. This data remains available even after the user closes the browser or navigates away from the page. The API provides a convenient way to maintain state and store user preferences without relying on server-side storage.
-
-## Exploring local storage Methods: A Practical Example
-
-Let's dive into some hands-on code examples to better understand how to leverage the power of localStorage. The API offers several methods for interaction, including setItem, getItem, removeItem, and clear. Consider the following code snippet:
-
-```js
-// Storing data using setItem
-localStorage.setItem('username', 'john_doe');
-
-// Retrieving data using getItem
-const storedUsername = localStorage.getItem('username');
-
-// Removing data using removeItem
-localStorage.removeItem('username');
-
-// Clearing all data
-localStorage.clear();
-```
-
-## Storing Complex Data in JavaScript with JSON Serialization
-
-While js localStorage excels at handling simple key-value pairs, it also supports more intricate data storage through JSON serialization. By utilizing JSON.stringify and JSON.parse, you can store and retrieve structured data like objects and arrays. Here's an example of storing a document:
-
-```js
-const user = {
- name: 'Alice',
- age: 30,
- email: 'alice@example.com'
-};
-
-// Storing a user object
-localStorage.setItem('user', JSON.stringify(user));
-
-// Retrieving and parsing the user object
-const storedUser = JSON.parse(localStorage.getItem('user'));
-```
-
-## Understanding the Limitations of local storage
-
-Despite its convenience, localStorage does come with a set of limitations that developers should be aware of:
-
-- **Non-Async Blocking API**: One significant drawback is that js localStorage operates as a non-async blocking API. This means that any operations performed on localStorage can potentially block the main thread, leading to slower application performance and a less responsive user experience.
-- **Limited Data Structure**: Unlike more advanced databases, localStorage is limited to a simple key-value store. This restriction makes it unsuitable for storing complex data structures or managing relationships between data elements.
-- **Stringification Overhead**: Storing [JSON data](./json-based-database.md) in localStorage requires stringifying the data before storage and parsing it when retrieved. This process introduces performance overhead, potentially slowing down operations by up to 10 times.
-- **Lack of Indexing**: localStorage lacks indexing capabilities, making it challenging to perform efficient searches or iterate over data based on specific criteria. This limitation can hinder applications that rely on complex data retrieval.
-- **Tab Blocking**: In a multi-tab environment, one tab's localStorage operations can impact the performance of other tabs by monopolizing CPU resources. You can reproduce this behavior by opening [this test file](https://pubkey.github.io/client-side-databases/database-comparison/index.html) in two browser windows and trigger localstorage inserts in one of them. You will observe that the indication spinner will stuck in both windows.
-- **Storage Limit**: Browsers typically impose a storage limit of [around 5 MiB](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria#web_storage) for each origin's localStorage.
-
-## Reasons to Still Use localStorage
-
-### Is localStorage Slow?
-
-Contrary to concerns about performance, the localStorage API in JavaScript is surprisingly fast when compared to alternative storage solutions like [IndexedDB or OPFS](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md). It excels in handling small key-value assignments efficiently. Due to its simplicity and direct integration with browsers, accessing and modifying localStorage data incur minimal overhead. For scenarios where quick and straightforward data storage is required, localStorage remains a viable option. For example RxDB uses localStorage in the [localStorage meta optimizer](../rx-storage-localstorage-meta-optimizer.md) to manage simple key values pairs while storing the "normal" documents inside of another storage like IndexedDB.
-
-## When Not to Use localStorage
-
-While localStorage offers convenience, it may not be suitable for every use case. Consider the following situations where alternatives might be more appropriate:
-
-- **Data Must Be Queryable**: If your application relies heavily on querying data based on specific criteria, localStorage might not provide the necessary querying capabilities. Complex data retrieval might lead to inefficient code and slow performance.
-- **Big JSON Documents**: Storing large JSON documents in localStorage can consume a significant amount of memory and degrade performance. It's essential to assess the size of the data you intend to store and consider more robust solutions for handling substantial datasets.
-- **Many Read/Write Operations**: Excessive read and write operations on localStorage can lead to performance bottlenecks. Other storage solutions might offer better performance and scalability for applications that require frequent data manipulation.
-- **Lack of Persistence**: If your application can function without persistent data across sessions, consider using in-memory data structures like `new Map()` or `new Set()`. These options offer speed and efficiency for transient data.
-
-## What to use instead of the localStorage API in JavaScript
-
-### localStorage vs IndexedDB
-
-While **localStorage** serves as a reliable storage solution for simpler data needs, it's essential to explore alternatives like **IndexedDB** when dealing with more complex requirements. **IndexedDB** is designed to store not only key-value pairs but also JSON documents. Unlike localStorage, which usually has a storage limit of around 5-10MB per domain, IndexedDB can handle significantly larger datasets. IndexDB with its support for indexing facilitates efficient querying, making range queries possible. However, it's worth noting that IndexedDB lacks observability, which is a feature unique to localStorage through the `storage` event. Also,
-complex queries can pose a challenge with IndexedDB, and while its performance is acceptable, IndexedDB can be [too slow](../slow-indexeddb.md) for some use cases.
-
-```js
-// localStorage can observe changes with the storage event.
-// This feature is missing in IndexedDB
-addEventListener("storage", (event) => {});
-```
-
-For those looking to harness the full power of IndexedDB with added capabilities, using wrapper libraries like [RxDB](https://rxdb.info/) is recommended. These libraries augment IndexedDB with features such as complex queries and observability, enhancing its usability for modern applications by providing a real database instead of only a key-value store.
-
-
-
-
-
-
-
-In summary when you compare IndexedDB vs localStorage, IndexedDB will win at any case where much data is handled while localStorage has better performance on small key-value datasets.
-
-### File System API (OPFS)
-Another intriguing option is the OPFS (File System API). This API provides direct access to an origin-based, sandboxed filesystem which is highly optimized for performance and offers in-place write access to its content.
-OPFS offers impressive performance benefits. However, working with the OPFS API can be complex, and it's only accessible within a **WebWorker**. To simplify its usage and extend its capabilities, consider using a wrapper library like [RxDB's OPFS RxStorage](../rx-storage-opfs.md), which builds a comprehensive database on top of the OPFS API. This abstraction allows you to harness the power of the OPFS API without the intricacies of direct usage.
-
-### localStorage vs Cookies
-Cookies, once a primary method of client-side data storage, have fallen out of favor in modern web development due to their limitations. While they can store data, they are about **100 times slower** when compared to the localStorage API. Additionally, cookies are included in the HTTP header, which can impact network performance. As a result, cookies are not recommended for data storage purposes in contemporary web applications.
-
-### localStorage vs WebSQL
-WebSQL, despite offering a SQL-based interface for client-side data storage, is a **deprecated technology** and should be avoided. Its API has been phased out of modern browsers, and it lacks the robustness of alternatives like IndexedDB. Moreover, WebSQL tends to be around 10 times slower than IndexedDB, making it a suboptimal choice for applications that demand efficient data manipulation and retrieval.
-
-### localStorage vs sessionStorage
-In scenarios where data persistence beyond a session is unnecessary, developers often turn to sessionStorage. This storage mechanism retains data only for the duration of a tab or browser session. It survives page reloads and restores, providing a handy solution for temporary data needs. However, it's important to note that sessionStorage is limited in scope and may not suit all use cases.
-
-### AsyncStorage for React Native
-For React Native developers, the [AsyncStorage API](https://reactnative.dev/docs/asyncstorage) is the go-to solution, mirroring the behavior of localStorage but with asynchronous support. Since not all JavaScript runtimes support localStorage, AsyncStorage offers a seamless alternative for data persistence in React Native applications.
-
-### `node-localstorage` for Node.js
-
-Because native localStorage is absent in the **Node.js** JavaScript runtime, you will get the error `ReferenceError: localStorage is not defined` in Node.js or node based runtimes like Next.js. The [node-localstorage npm package](https://github.com/lmaccherone/node-localstorage) bridges the gap. This package replicates the browser's localStorage API within the Node.js environment, ensuring consistent and compatible data storage capabilities.
-
-## localStorage in browser extensions
-
-While browser extensions for chrome and firefox support the localStorage API, it is not recommended to use it in that context to store extension-related data. The browser will clear the data in many scenarios like when the users clear their browsing history.
-
-Instead the [Extension Storage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage#properties) should be used for browser extensions.
-In contrast to localStorage, the storage API works `async` and all operations return a Promise. Also it provides automatic sync to replicate data between all instances of that browser that the user is logged into. The storage API is even able to storage JSON-ifiable objects instead of plain strings.
-
-```ts
-// Using the storage API in chrome
-
-await chrome.storage.local.set({ foobar: {nr: 1} });
-
-const result = await chrome.storage.local.get('foobar');
-console.log(result.foobar); // {nr: 1}
-```
-
-## localStorage in Deno and Bun
-
-The **Deno** JavaScript runtime has a working localStorage API so running `localStorage.setItem()` and the other methods, will just work and the locally stored data is persisted across multiple runs.
-
-**Bun** does not support the localStorage JavaScript API. Trying to use `localStorage` will error with `ReferenceError: Can't find variable: localStorage`. To store data locally in Bun, you could use the `bun:sqlite` module instead or directly use a in-JavaScript database with Bun support like [RxDB](https://rxdb.info/).
-
-## Conclusion: Choosing the Right Storage Solution
-In the world of modern web development, **localStorage** serves as a valuable tool for lightweight data storage. Its simplicity and speed make it an excellent choice for small key-value assignments. However, as application complexity grows, developers must assess their storage needs carefully. For scenarios that demand advanced querying, complex data structures, or high-volume operations, alternatives like IndexedDB, wrapper libraries with additional features like [RxDB](../), or platform-specific APIs offer more robust solutions. By understanding the strengths and limitations of various storage options, developers can make informed decisions that pave the way for efficient and scalable applications.
-
-## Follow up
-
-- Learn how to store and query data with RxDB in the [RxDB Quickstart](../quickstart.md)
-- [Why IndexedDB is slow and how to fix it](../slow-indexeddb.md)
-- [RxStorage performance comparison](../rx-storage-performance.md)
diff --git a/docs/articles/mobile-database.html b/docs/articles/mobile-database.html
deleted file mode 100644
index ee021606f54..00000000000
--- a/docs/articles/mobile-database.html
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
-
-
-Real-Time & Offline - RxDB for Mobile Apps | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Mobile Database - RxDB as Database for Mobile Applications
-
In today's digital landscape, mobile applications have become an integral part of our lives. From social media platforms to e-commerce solutions, mobile apps have transformed the way we interact with digital services. At the heart of any mobile app lies the database, a critical component responsible for storing, retrieving, and managing data efficiently. In this article, we will delve into the world of mobile databases, exploring their significance, challenges, and the emergence of RxDB as a powerful database solution for hybrid app development in frameworks like React Native and Capacitor.
Mobile databases are specialized software systems designed to handle data storage and management for mobile applications. These databases are optimized for the unique requirements of mobile environments, which often include limited device resources, fluctuations in network connectivity, and the need for offline functionality.
-
There are various types of mobile databases available, each with its own strengths and use cases. Local databases, such as SQLite and Realm, reside directly on the user's device, providing offline capabilities and faster data access. Cloud-based databases, like Firebase Realtime Database and Amazon DynamoDB, rely on remote servers to store and retrieve data, enabling synchronization across multiple devices. Hybrid databases, as the name suggests, combine the benefits of both local and cloud-based approaches, offering a balance between offline functionality and data synchronization.
-
Introducing RxDB: A Paradigm Shift in Mobile Database Solutions
-
-
RxDB, also known as Reactive Database, has emerged as a game-changer in the realm of mobile databases. Built on top of popular web technologies like JavaScript, TypeScript, and RxJS (Reactive Extensions for JavaScript), RxDB provides an elegant solution for seamless offline-first capabilities and real-time data synchronization in mobile applications.
-
Benefits of RxDB for Hybrid App Development
-
-
-
Offline-First Approach: One of the major advantages of RxDB is its ability to work in an offline mode. It allows mobile applications to store and access data locally, ensuring uninterrupted functionality even when the network connection is weak or unavailable. The database automatically syncs the data with the server once the connection is reestablished, guaranteeing data consistency.
-
-
-
Real-Time Data Synchronization: RxDB leverages the power of real-time data synchronization, making it an excellent choice for applications that require collaborative features or live updates. It uses the concept of change streams to detect modifications made to the database and instantly propagates those changes across connected devices. This real-time synchronization enables seamless collaboration and enhances user experience.
-
-
-
Reactive Programming Paradigm: RxDB embraces the principles of reactive programming, which simplifies the development process by handling asynchronous events and data streams. By leveraging RxJS observables, developers can write concise, declarative code that reacts to changes in data, ensuring a highly responsive user experience. The reactive programming paradigm enhances code maintainability, scalability, and testability.
-
-
-
Easy Integration with Hybrid App Frameworks: RxDB seamlessly integrates with popular hybrid app development frameworks like React Native and Capacitor. This compatibility allows developers to leverage the existing ecosystem and tools of these frameworks, making the transition to RxDB smoother and more efficient. By utilizing RxDB within these frameworks, developers can harness the power of a robust database solution without sacrificing the advantages of hybrid app development.
-
-
-
Cross-Platform Support: RxDB enables developers to build cross-platform mobile applications that run seamlessly on both iOS and Android devices. This versatility eliminates the need for separate database implementations for different platforms, saving development time and effort. With RxDB, developers can focus on building a unified codebase and delivering a consistent user experience across platforms.
Offline-First Applications: RxDB is an ideal choice for applications that heavily rely on offline functionality. Whether it's a note-taking app, a task manager, or a survey application, RxDB ensures that users can continue working even when connectivity is compromised. The seamless synchronization capabilities of RxDB ensure that changes made offline are automatically propagated once the device reconnects to the internet.
-
-
-
Real-Time Collaboration: Applications that require real-time collaboration, such as messaging platforms or collaborative editing tools, can greatly benefit from RxDB. The real-time synchronization capabilities enable multiple users to work on the same data simultaneously, ensuring that everyone sees the latest updates in real-time.
-
-
-
Data-Intensive Applications: RxDB's performance and scalability make it suitable for data-intensive applications that handle large datasets or complex data structures. Whether it's a media-rich app, a data visualization tool, or an analytics platform, RxDB can handle the heavy lifting and provide a smooth user experience.
-
-
-
Cross-Platform Applications: Hybrid app frameworks like React Native and Capacitor have gained popularity due to their ability to build cross-platform applications. By utilizing RxDB within these frameworks, developers can create a unified codebase that runs seamlessly on both iOS and Android, significantly reducing development time and effort.
Mobile databases play a vital role in the performance and functionality of mobile applications. RxDB, with its offline-first approach, real-time data synchronization, and seamless integration with hybrid app development frameworks like React Native and Capacitor, offers a robust solution for managing data in mobile apps. By leveraging the power of reactive programming, RxDB empowers developers to build highly responsive, scalable, and cross-platform applications that deliver an exceptional user experience. With its versatility and ease of use, RxDB is undoubtedly a database solution worth considering for hybrid app development. Embrace the power of RxDB and unlock the full potential of your mobile applications.
-
-
\ No newline at end of file
diff --git a/docs/articles/mobile-database.md b/docs/articles/mobile-database.md
deleted file mode 100644
index 9dc61869a9e..00000000000
--- a/docs/articles/mobile-database.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Real-Time & Offline - RxDB for Mobile Apps
-
-> Explore RxDB as your reliable mobile database. Enjoy offline-first capabilities, real-time sync, and seamless integration for hybrid app development.
-
-# Mobile Database - RxDB as Database for Mobile Applications
-
-In today's digital landscape, mobile applications have become an integral part of our lives. From social media platforms to e-commerce solutions, mobile apps have transformed the way we interact with digital services. At the heart of any mobile app lies the database, a critical component responsible for storing, retrieving, and managing data efficiently. In this article, we will delve into the world of mobile databases, exploring their significance, challenges, and the emergence of [RxDB](https://rxdb.info/) as a powerful database solution for hybrid app development in frameworks like React Native and Capacitor.
-
-## Understanding Mobile Databases
-
-Mobile databases are specialized software systems designed to handle data storage and management for mobile applications. These databases are optimized for the unique requirements of mobile environments, which often include limited device resources, fluctuations in network connectivity, and the need for offline functionality.
-
-There are various types of mobile databases available, each with its own strengths and use cases. Local databases, such as SQLite and Realm, reside directly on the user's device, providing offline capabilities and faster data access. Cloud-based databases, like [Firebase Realtime Database](./realtime-database.md) and Amazon DynamoDB, rely on remote servers to store and retrieve data, enabling synchronization across multiple devices. Hybrid databases, as the name suggests, combine the benefits of both local and cloud-based approaches, offering a balance between offline functionality and data synchronization.
-
-## Introducing RxDB: A Paradigm Shift in Mobile Database Solutions
-
-
-
-
-
-
-
-[RxDB](https://rxdb.info/), also known as Reactive Database, has emerged as a game-changer in the realm of mobile databases. Built on top of popular web technologies like JavaScript, TypeScript, and RxJS (Reactive Extensions for JavaScript), RxDB provides an elegant solution for seamless offline-first capabilities and real-time data synchronization in mobile applications.
-
-Benefits of RxDB for Hybrid App Development
-
-1. Offline-First Approach: One of the major advantages of RxDB is its ability to work in an offline mode. It allows mobile applications to store and access data locally, ensuring uninterrupted functionality even when the network connection is weak or unavailable. The database automatically syncs the data with the server once the connection is reestablished, guaranteeing data consistency.
-
-2. [Real-Time Data Synchronization](../replication.md): RxDB leverages the power of real-time data synchronization, making it an excellent choice for applications that require collaborative features or live updates. It uses the concept of change streams to detect modifications made to the database and instantly propagates those changes across connected devices. This real-time synchronization enables seamless collaboration and enhances user experience.
-
-3. Reactive Programming Paradigm: RxDB embraces the principles of reactive programming, which simplifies the development process by handling asynchronous events and data streams. By leveraging RxJS observables, developers can write concise, declarative code that reacts to changes in data, ensuring a highly responsive user experience. The reactive programming paradigm enhances code maintainability, scalability, and testability.
-
-4. Easy Integration with Hybrid App Frameworks: RxDB seamlessly integrates with popular hybrid app development frameworks like [React Native](../react-native-database.md) and [Capacitor](../capacitor-database.md). This compatibility allows developers to leverage the existing ecosystem and tools of these frameworks, making the transition to RxDB smoother and more efficient. By utilizing RxDB within these frameworks, developers can harness the power of a robust database solution without sacrificing the advantages of hybrid app development.
-
-5. Cross-Platform Support: RxDB enables developers to build cross-platform mobile applications that run seamlessly on both iOS and Android devices. This versatility eliminates the need for separate database implementations for different platforms, saving development time and effort. With RxDB, developers can focus on building a unified codebase and delivering a consistent user experience across platforms.
-
-## Use Cases for RxDB in Hybrid App Development
-
-1. [Offline-First Applications](../offline-first.md): [RxDB](https://rxdb.info/) is an ideal choice for applications that heavily rely on offline functionality. Whether it's a note-taking app, a task manager, or a survey application, RxDB ensures that users can continue working even when connectivity is compromised. The seamless synchronization capabilities of RxDB ensure that changes made offline are automatically propagated once the device reconnects to the internet.
-
-2. Real-Time Collaboration: Applications that require real-time collaboration, such as messaging platforms or collaborative editing tools, can greatly benefit from RxDB. The real-time synchronization capabilities enable multiple users to work on the same data simultaneously, ensuring that everyone sees the latest updates in real-time.
-
-3. Data-Intensive Applications: RxDB's performance and scalability make it suitable for data-intensive applications that handle large datasets or complex data structures. Whether it's a media-rich app, a data visualization tool, or an analytics platform, RxDB can handle the heavy lifting and provide a smooth user experience.
-
-4. Cross-Platform Applications: Hybrid app frameworks like React Native and Capacitor have gained popularity due to their ability to build cross-platform applications. By utilizing RxDB within these frameworks, developers can create a unified codebase that runs seamlessly on both iOS and Android, significantly reducing development time and effort.
-
-## Conclusion
-
-Mobile databases play a vital role in the performance and functionality of mobile applications. RxDB, with its offline-first approach, real-time data synchronization, and seamless integration with hybrid app development frameworks like React Native and Capacitor, offers a robust solution for managing data in mobile apps. By leveraging the power of reactive programming, RxDB empowers developers to build highly responsive, scalable, and cross-platform applications that deliver an exceptional user experience. With its versatility and ease of use, RxDB is undoubtedly a database solution worth considering for hybrid app development. Embrace the power of RxDB and unlock the full potential of your mobile applications.
diff --git a/docs/articles/offline-database.html b/docs/articles/offline-database.html
deleted file mode 100644
index 5e39c99b5bf..00000000000
--- a/docs/articles/offline-database.html
+++ /dev/null
@@ -1,210 +0,0 @@
-
-
-
-
-
-RxDB – The Ultimate Offline Database with Sync and Encryption | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
RxDB – The Ultimate Offline Database with Sync and Encryption
-
When building modern applications, a reliable offline database can make all the difference. Users need fast, uninterrupted access to data, even without an internet connection, and they need that data to stay secure. RxDB meets these requirements by providing a local-first architecture, real-time sync to any backend, and optional encryption for sensitive fields.
-
In this article, we'll cover:
-
-
Why an offline database approach significantly improves user experience
Offline-first or local-first software stores data directly on the client device. This strategy isn’t just about surviving network outages; it also makes your application faster, more user-friendly, and better at handling multiple usage scenarios.
Applications that call remote servers for every request inevitably show loading spinners. With an offline database, read and write operations happen locally—providing near-instant feedback. Users no longer stare at progress indicators or wait for server responses, resulting in a smoother and more fluid experience.
Many websites mishandle data across multiple browser tabs. In an offline database, all tabs share the same local datastore. If the user updates data in one tab (like completing a to-do item), changes instantly reflect in every other tab. This removes complex multi-window synchronization problems.
Apps that rely on a purely server-driven approach often show stale data unless they add a separate real-time push system (like websockets). Local-first solutions with built-in replication essentially get real-time updates “for free.” Once the server sends any changes, your local offline database updates—keeping your UI live and accurate.
In a traditional app, every interaction triggers a request to the server, scaling up resource usage quickly as traffic grows. Offline-first setups replicate data to the client once, and subsequent local reads or writes do not stress the backend. Your server usage grows with the amount of data—rather than every user action—leading to more efficient scaling.
-
5. Simpler Development: Fewer Endpoints, No Extra State Library
-
Typical apps require numerous REST endpoints and possibly a client-side state manager (like Redux) to handle data flow. If you adopt an offline database, you can replicate nearly everything to the client. The local DB becomes your single source of truth, and you may skip advanced state libraries altogether.
-
-
Introducing RxDB – A Powerful Offline Database Solution
-
RxDB (Reactive Database) is a NoSQL JavaScript database that lives entirely in your client environment. It’s optimized for:
Below is a short demo of how to create an RxDB database, add a collection, and observe a query. You can expand upon this to enable encryption or full sync.
Now the tasks collection is ready to store data offline. You could also replicate it to a backend, encrypt certain fields, or utilize more advanced features like conflict resolution.
RxDB uses a Sync Engine that pushes local changes to the server and pulls remote updates back down. This ensures local data is always fresh and that the server has the latest offline edits once the device reconnects.
-
Multiple Plugins exist to handle various backends or replication methods:
You pick the plugin that fits your stack, and RxDB handles everything from conflict detection to event emission, allowing you to focus on building your user-facing features.
-
import { replicateRxCollection } from 'rxdb/plugins/replication';
-
-replicateRxCollection({
- collection: db.tasks,
- replicationIdentifier: 'tasks-sync',
- pull: { /* fetch updates from server */ },
- push: { /* send local writes to server */ },
- live: true // keep them in sync constantly
-});
import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js';
-
-async function initSecureDB() {
- // Wrap the storage with crypto-js encryption
- const encryptedStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageLocalstorage()
- });
-
- // Create database with a password
- const db = await createRxDatabase({
- name: 'secureOfflineDB',
- storage: encryptedStorage,
- password: 'myTopSecretPassword'
- });
-
- // Define an encrypted collection
- await db.addCollections({
- userSecrets: {
- schema: {
- title: 'encrypted user data',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- secretData: { type: 'string' }
- },
- required: ['id'],
- encrypted: ['secretData'] // field is encrypted at rest
- }
- }
- });
-
- return db;
-}
-
When the device is off or the database file is extracted, secretData remains unreadable without the specified password. This ensures only authorized parties can access sensitive fields, even in offline scenarios.
Integrating an offline database approach into your app delivers near-instant interactions, true multi-tab data consistency, automatic real-time updates, and reduced server dependencies. By choosing RxDB, you gain:
-
-
Offline-first local storage
-
Flexible replication to various backends
-
Encryption of sensitive fields
-
Reactive queries for real-time UI updates
-
-
RxDB transforms how you build and scale apps—no more loading spinners, no more stale data, no more complicated offline handling. Everything is local, synced, and secured.
-
Continue your learning path:
-
-
-
Explore the RxDB Ecosystem
-Dive into additional features like Compression or advanced Conflict Handling to optimize your offline database.
-
-
-
Learn More About Offline-First
-Read our Offline First documentation for a deeper understanding of why local-first architectures improve user experience and reduce server load.
-
-
-
Join the Community
-Have questions or feedback? Connect with us on the RxDB Chat or open an issue on GitHub.
By adopting an offline database approach with RxDB, you unlock speed, reliability, and security for your applications—leading to a truly seamless user experience.
-
-
\ No newline at end of file
diff --git a/docs/articles/offline-database.md b/docs/articles/offline-database.md
deleted file mode 100644
index ae81f0111b6..00000000000
--- a/docs/articles/offline-database.md
+++ /dev/null
@@ -1,208 +0,0 @@
-# RxDB – The Ultimate Offline Database with Sync and Encryption
-
-> Discover how RxDB serves as a powerful offline database, offering real-time synchronization, secure encryption, and an offline-first approach for modern web and mobile apps.
-
-# RxDB – The Ultimate Offline Database with Sync and Encryption
-
-When building modern applications, a reliable **offline database** can make all the difference. Users need fast, uninterrupted access to data, even without an internet connection, and they need that data to stay secure. **RxDB** meets these requirements by providing a **local-first** architecture, **real-time sync** to any backend, and optional **encryption** for sensitive fields.
-
-In this article, we'll cover:
-- Why an **offline database** approach significantly improves user experience
-- How RxDB’s **sync** and **encryption** features work
-- Step-by-step guidance on getting started
-
----
-
-## Why Choose an Offline Database?
-
-[Offline-first](../offline-first.md) or **local-first** software stores data directly on the client device. This strategy isn’t just about surviving network outages; it also makes your application faster, more user-friendly, and better at handling multiple usage scenarios.
-
-### 1. Zero Loading Spinners
-Applications that call remote servers for every request inevitably show loading spinners. With an offline database, read and write operations happen locally—providing near-instant feedback. Users no longer stare at progress indicators or wait for server responses, resulting in a smoother and more fluid experience.
-
-
-
-### 2. Multi-Tab Consistency
-Many websites mishandle data across multiple browser tabs. In an offline database, all tabs share the same local datastore. If the user updates data in one tab (like completing a to-do item), changes instantly reflect in every other tab. This removes complex multi-window synchronization problems.
-
-
-
-### 3. Real-Time Data Feeds
-Apps that rely on a purely server-driven approach often show stale data unless they add a separate real-time push system (like websockets). Local-first solutions with built-in replication essentially get real-time updates “for free.” Once the server sends any changes, your local offline database updates—keeping your UI live and accurate.
-
-### 4. Reduced Server Load
-In a traditional app, every interaction triggers a request to the server, scaling up resource usage quickly as traffic grows. Offline-first setups replicate data to the client once, and subsequent local reads or writes do not stress the backend. Your server usage grows with the amount of data—rather than every user action—leading to more efficient scaling.
-
-### 5. Simpler Development: Fewer Endpoints, No Extra State Library
-Typical apps require numerous REST endpoints and possibly a client-side state manager (like Redux) to handle data flow. If you adopt an offline database, you can replicate nearly everything to the client. The local DB becomes your single source of truth, and you may skip advanced state libraries altogether.
-
-
-
-
-
-
-
-## Introducing RxDB – A Powerful Offline Database Solution
-
-**RxDB (Reactive Database)** is a **NoSQL** JavaScript database that lives entirely in your client environment. It’s optimized for:
-
-- **Offline-first usage**
-- **Reactive queries** (your UI updates in real time)
-- **Flexible replication** with various backends
-- **Field-level encryption** to protect sensitive data
-
-You can run RxDB in:
-- **Browsers** ([IndexedDB](../rx-storage-indexeddb.md), [OPFS](../rx-storage-opfs.md))
-- **Mobile hybrid apps** ([Ionic](./ionic-database.md), [Capacitor](../capacitor-database.md))
-- **Native modules** ([React Native](../react-native-database.md))
-- **Desktop environments** ([Electron](../electron-database.md))
-- **Node.js** [Servers](../rx-server.md) or Scripts
-
-Wherever your JavaScript executes, RxDB can serve as a robust offline database.
-
----
-
-## Quick Setup Example
-
-Below is a short demo of how to create an RxDB [database](../rx-database.md), add a [collection](../rx-collection.md), and observe a [query](../rx-query.md). You can expand upon this to enable encryption or full sync.
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-async function initDB() {
- // Create a local offline database
- const db = await createRxDatabase({
- name: 'myOfflineDB',
- storage: getRxStorageLocalstorage()
- });
-
- // Add collections
- await db.addCollections({
- tasks: {
- schema: {
- title: 'tasks schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- title: { type: 'string' },
- done: { type: 'boolean' }
- }
- }
- }
- });
-
- // Observe changes in real time
- db.tasks
- .find({ selector: { done: false } })
- .$ // returns an observable that emits whenever the result set changes
- .subscribe(undoneTasks => {
- console.log('Currently undone tasks:', undoneTasks);
- });
-
- return db;
-}
-```
-
-Now the `tasks` collection is ready to store data offline. You could also [replicate](../replication.md) it to a backend, encrypt certain fields, or utilize more advanced features like conflict resolution.
-
-## How Offline Sync Works in RxDB
-
-RxDB uses a [Sync Engine](../replication.md) that pushes local changes to the server and pulls remote updates back down. This ensures local data is always fresh and that the server has the latest offline edits once the device reconnects.
-
-**Multiple Plugins** exist to handle various backends or replication methods:
-- [CouchDB](../replication-couchdb.md) or **PouchDB**
-- [Google Firestore](./firestore-alternative.md)
-- [GraphQL](../replication-graphql.md) endpoints
-- REST / [HTTP](../replication-http.md)
-- **WebSocket** or [WebRTC](../replication-webrtc.md) (for peer-to-peer sync)
-
-You pick the plugin that fits your stack, and RxDB handles everything from conflict detection to event emission, allowing you to focus on building your user-facing features.
-
-```ts
-import { replicateRxCollection } from 'rxdb/plugins/replication';
-
-replicateRxCollection({
- collection: db.tasks,
- replicationIdentifier: 'tasks-sync',
- pull: { /* fetch updates from server */ },
- push: { /* send local writes to server */ },
- live: true // keep them in sync constantly
-});
-```
-
-## Securing Your Offline Database with Encryption
-Local data can be a risk if it’s sensitive or personal. RxDB offers [encryption plugins](../encryption.md) to keep specific document fields secure at rest.
-
-#### Encryption Example
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js';
-
-async function initSecureDB() {
- // Wrap the storage with crypto-js encryption
- const encryptedStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageLocalstorage()
- });
-
- // Create database with a password
- const db = await createRxDatabase({
- name: 'secureOfflineDB',
- storage: encryptedStorage,
- password: 'myTopSecretPassword'
- });
-
- // Define an encrypted collection
- await db.addCollections({
- userSecrets: {
- schema: {
- title: 'encrypted user data',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- secretData: { type: 'string' }
- },
- required: ['id'],
- encrypted: ['secretData'] // field is encrypted at rest
- }
- }
- });
-
- return db;
-}
-```
-
-When the device is off or the database file is extracted, `secretData` remains unreadable without the specified password. This ensures only authorized parties can access sensitive fields, even in offline scenarios.
-
-## Follow Up
-
-Integrating an offline database approach into your app delivers near-instant interactions, true multi-tab data consistency, automatic real-time updates, and reduced server dependencies. By choosing RxDB, you gain:
-
-- Offline-first local storage
-- Flexible replication to various backends
-- Encryption of sensitive fields
-- Reactive queries for real-time UI updates
-
-RxDB transforms how you build and scale apps—no more loading spinners, no more stale data, no more complicated offline handling. Everything is local, synced, and secured.
-
-Continue your learning path:
-
-- **Explore the RxDB Ecosystem**
- Dive into additional features like [Compression](../key-compression.md) or advanced [Conflict Handling](../transactions-conflicts-revisions.md#custom-conflict-handler) to optimize your offline database.
-
-- **Learn More About Offline-First**
- Read our [Offline First documentation](../offline-first.md) for a deeper understanding of why local-first architectures improve user experience and reduce server load.
-
-- **Join the Community**
- Have questions or feedback? Connect with us on the [RxDB Chat](/chat/) or open an issue on [GitHub](/code/).
-
-- **Upgrade to Premium**
- If you need high-performance features—like [SQLite storage](../rx-storage-sqlite.md) for mobile or the [Web Crypto-based encryption plugin](/premium/)—consider our premium offerings.
-
-By adopting an offline database approach with RxDB, you unlock speed, reliability, and security for your applications—leading to a truly seamless user experience.
diff --git a/docs/articles/optimistic-ui.html b/docs/articles/optimistic-ui.html
deleted file mode 100644
index bb5435cb7f2..00000000000
--- a/docs/articles/optimistic-ui.html
+++ /dev/null
@@ -1,203 +0,0 @@
-
-
-
-
-
-Building an Optimistic UI with RxDB | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
An Optimistic User Interface (UI) is a design pattern that provides instant feedback to the user by assuming that an operation or server call will succeed. Instead of showing loading spinners or waiting for server confirmations, the UI immediately reflects the user's intended action and later reconciles the displayed data with the actual server response. This approach drastically improves perceived performance and user satisfaction.
Optimistic UIs offer a host of advantages, from improving the user experience to streamlining the underlying infrastructure. Below are some key reasons why an optimistic approach can revolutionize your application's performance and scalability.
No loading spinners, near-zero latency: Users perceive their actions as instant. Any actual network delays or slow server operations can be handled behind the scenes.
-
Offline capability: Optimistic UI pairs perfectly with offline-first apps. Users can continue to interact with the application even when offline, and changes will be synced automatically once the network is available again.
Fewer server endpoints: Instead of sending a separate HTTP request for every single user interaction, you can batch updates and sync them in bulk.
-
Less server load: By handling changes locally and syncing in batches, you reduce the volume of server round-trips.
-
Automated error handling: If a request fails or a document is in conflict, RxDB's replication mechanism can seamlessly retry and resolve conflicts in the background, without requiring a separate endpoint or manual user intervention.
A local database is the heart of an Optimistic UI. With RxDB, all application state is stored locally, ensuring seamless and instant updates. You can choose from multiple storage backends based on your runtime - check out RxDB Storage Options to see which engines (IndexedDB, SQLite, or custom) suit your environment best.
-
-
-
Instant Writes: When users perform an action (like clicking a button or submitting a form), the changes are written directly to the local RxDB database. This immediate local write makes the UI feel snappy and removes the dependency on instantaneous server responses.
-
-
-
Offline-First: Because data is managed locally, your app continues to operate smoothly even without an internet connection. Users can view, create, and update data at any time, assured that changes will sync automatically once they're back online.
RxDB's core is built around observables that react to any state changes - whether from local writes or incoming replication from the server.
-
-
Automatic UI refresh: Any query or document subscription in RxDB automatically notifies your UI layer when data changes. There's no need to manually poll or refetch.
-
Cross-tab updates: If you have the same RxDB database open in multiple browser tabs, changes in one tab instantly propagate to the others.
-
-
-
-
Event-Reduce Algorithm: Under the hood, RxDB uses the event-reduce algorithm to minimize overhead. Instead of re-running expensive queries, RxDB calculates the smallest possible updates needed to keep query results accurate - further boosting real-time performance.
While local storage is key to an Optimistic UI, most applications ultimately need to sync with a remote back end. RxDB offers a powerful replication system that can sync your local data with virtually any server/database in the background:
-
-
Incremental and real-time: RxDB continuously pushes local changes to the server when a network is available and fetches server updates as they happen.
-
Conflict resolution: If changes happen offline or multiple clients update the same data, RxDB detects conflicts and makes it straightforward to resolve them.
-
Flexible transport: Beyond simple HTTP polling, you can incorporate WebSockets, Server-Sent Events (SSE), or other protocols for instant, server-confirmed changes broadcast to all connected clients. See this guide to learn more.
-
-
By combining local-first data handling with real-time synchronization, RxDB delivers most of what an Optimistic UI needs - right out of the box. The result is a seamless user experience where interactions never feel blocked by slow networks, and any conflicts or final validations are quietly handled in the background.
Offline-first approach: All writes are initially stored in the local database. When connectivity returns, RxDB's replication automatically pushes changes to the server.
-
Conflict resolution: If multiple clients edit the same documents while offline, conflicts are automatically detected and can be resolved gracefully (more on conflicts below).
For truly real-time communication - where server-confirmed changes instantly reach all clients - you can go beyond simple HTTP polling. Use WebSockets, Server-Sent Events (SSE), or other streaming protocols to broadcast updates the moment they occur. This pattern excels in scenarios like chats, collaborative editors, or dynamic dashboards.
-
To learn more about these protocols and their integration with RxDB, check out this guide.
In React, you can utilize signals or other state management tools. For instance, if we have an RxDB extension that exposes a signal:
-
import React from 'react';
-
-function MyComponent({ myCollection }) {
- // .find().$$ provides a signal that updates whenever data changes
- const docsSignal = myCollection.find().$$;
-
- return (
- <ul>
- {docs.map((doc) => (
- <li key={doc.id}>{doc.name}</li>
- ))}
- </ul>
- );
-}
-
-export default MyComponent;
-
When you call docsSignal.value or use a hook like useSignal, it pulls the latest value from the RxDB query. Whenever the collection updates, the signal emits the new data, and React re-renders the component instantly.
While Optimistic UIs feel snappy, there are some caveats:
-
-
-
Conflict Resolution:
-With an optimistic approach, multiple offline devices might edit the same data. When syncing back, conflicts occur that must be merged. RxDB uses revisions to detect and handle these conflicts.
-
-
-
User Confusion:
-Users may see changes that haven't yet been confirmed by the server. If a subsequent server validation fails, the UI must revert to a previous state. Clear visual feedback or user notifications help reduce confusion.
-
-
-
Server Compatibility:
-The server must be capable of storing and returning revision metadata (for instance, a timestamp or versioning system). Check out RxDB's replication docs for details on how to structure your back end.
-
-
-
Storage Limits:
-Storing data in the client has practical size limits. IndexedDB or other client-side storages have constraints (though usually quite large). See storage comparisons.
Last Write to Server Wins:
-A simplest-possible method: whatever update reaches the server last overrides previous data. Good for non-critical data like “like" counts or ephemeral states.
-
Revision-Based Merges:
-Use revision numbers or timestamps to track concurrent edits. Merge them intelligently by combining fields or choosing the latest sub-document changes. This is ideal for collaborative apps where you don't want to overwrite entire records.
-
User Prompts:
-In certain workflows (e.g., shipping forms, e-commerce checkout), you may need to notify the user about conflicts and let them choose which version to keep.
-
First Write to Server Wins (RxDB Default):
-RxDB's default approach is to let the first successful push define the latest version. Any incoming push with an outdated revision triggers a conflict that must be resolved on the client side. Learn more at here.
Real-time interactions like chat apps, social feeds, or “Likes."
-Situations where high success rates of operations are expected (most writes don't fail).
-
Apps that need an offline-first approach or handle intermittent connectivity gracefully.
-
-
-
-
When Not to Use
-
-
Large, complex transactions with high failure rates.
-
Scenarios requiring heavy server validations or approvals (for example, financial transactions with complex rules).
-
Workflows where immediate feedback could mislead users about an operation's success probability.
-
-
-
-
Assessing Risk
-
-
Consider the likelihood that a user's action might fail. If it's very low, optimistic UI is often best.
-
If frequent failures or complex validations occur, consider a hybrid approach: partial optimistic updates for some actions, while more critical operations rely on immediate server confirmation.
Ready to start building your own Optimistic UI with RxDB? Here are some next steps:
-
-
-
Do the RxDB Quickstart
-If you're brand new to RxDB, the quickstart guide will walk you through installation and setting up your first project.
-
-
-
Check Out the Demo App
-A live RxDB Quickstart Demo showcases optimistic updates and real-time syncing. Explore the code to see how it works.
-
-
-
Star the GitHub Repo
-Show your support for RxDB by starring the RxDB GitHub Repository.
-
-
-
By combining RxDB's powerful offline-first capabilities with the principles of an Optimistic UI, you can deliver snappy, near-instant user interactions that keep your users engaged - no matter the network conditions. Get started today and give your users the experience they deserve!
-
-
\ No newline at end of file
diff --git a/docs/articles/optimistic-ui.md b/docs/articles/optimistic-ui.md
deleted file mode 100644
index 68a3d0286f1..00000000000
--- a/docs/articles/optimistic-ui.md
+++ /dev/null
@@ -1,178 +0,0 @@
-# Building an Optimistic UI with RxDB
-
-> Learn how to build an Optimistic UI with RxDB for instant and reliable UI updates on user interactions
-
-# Building an Optimistic UI with RxDB
-
-An **Optimistic User Interface (UI)** is a design pattern that provides instant feedback to the user by **assuming** that an operation or server call will succeed. Instead of showing loading spinners or waiting for server confirmations, the UI immediately reflects the user's intended action and later reconciles the displayed data with the actual server response. This approach drastically improves perceived performance and user satisfaction.
-
-## Benefits of an Optimistic UI
-
-Optimistic UIs offer a host of advantages, from improving the user experience to streamlining the underlying infrastructure. Below are some key reasons why an optimistic approach can revolutionize your application's performance and scalability.
-
-### Better User Experience with Optimistic UI
-- **No loading spinners, [near-zero latency](./zero-latency-local-first.md)**: Users perceive their actions as instant. Any actual network delays or slow server operations can be handled behind the scenes.
-- **Offline capability**: Optimistic UI pairs perfectly with offline-first apps. Users can continue to interact with the application even when offline, and changes will be synced automatically once the network is available again.
-
-
-
-### Better Scaling and Easier to Implement
-- **Fewer server endpoints**: Instead of sending a separate HTTP request for every single user interaction, you can batch updates and sync them in bulk.
-- **Less server load**: By handling changes locally and syncing in batches, you reduce the volume of server round-trips.
-- **Automated error handling**: If a request fails or a document is in conflict, RxDB's [replication](../replication.md) mechanism can seamlessly retry and resolve conflicts in the background, without requiring a separate endpoint or manual user intervention.
-
-
-
-
-
-
-
-## Building Optimistic UI Apps with RxDB
-
-Now that we know what an optimistic UI is, lets build one with RxDB.
-
-### Local Database: The Backbone of an Optimistic UI
-
-A local database is the heart of an Optimistic UI. With RxDB, **all application state** is stored locally, ensuring seamless and instant updates. You can choose from multiple storage backends based on your runtime - check out [RxDB Storage Options](../rx-storage.md) to see which engines (IndexedDB, SQLite, or custom) suit your environment best.
-
-- **Instant Writes**: When users perform an action (like clicking a button or submitting a form), the changes are written directly to the local RxDB database. This immediate local write makes the UI feel snappy and removes the dependency on instantaneous server responses.
-
-- [Offline-First](../offline-first.md): Because data is managed locally, your app continues to operate smoothly even without an internet connection. Users can view, create, and update data at any time, assured that changes will sync automatically once they're back online.
-
-### Real-Time UI Changes on Updates
-
-RxDB's core is built around observables that react to any state changes - whether from local writes or incoming replication from the server.
-
-- **Automatic UI refresh**: Any query or document subscription in RxDB automatically notifies your UI layer when data changes. There's no need to manually poll or refetch.
-- **Cross-tab updates**: If you have the same RxDB database open in multiple [browser](./browser-database.md) tabs, changes in one tab instantly propagate to the others.
-
-
-
-- **Event-Reduce Algorithm**: Under the hood, RxDB uses the [event-reduce algorithm](https://github.com/pubkey/event-reduce) to minimize overhead. Instead of re-running expensive queries, RxDB calculates the smallest possible updates needed to keep query results accurate - further boosting real-time performance.
-
-### Replication with a Server
-
-While local storage is key to an Optimistic UI, most applications ultimately need to sync with a remote back end. RxDB offers a [powerful replication system](../replication.md) that can sync your local data with virtually any server/database in the background:
-- **Incremental and real-time**: RxDB continuously pushes local changes to the server when a network is available and fetches server updates as they happen.
-- **Conflict resolution**: If changes happen offline or multiple clients update the same data, RxDB detects conflicts and makes it straightforward to resolve them.
-- **Flexible transport**: Beyond simple HTTP polling, you can incorporate WebSockets, Server-Sent Events (SSE), or other protocols for instant, server-confirmed changes broadcast to all connected clients. See [this guide](./websockets-sse-polling-webrtc-webtransport.md) to learn more.
-
-By combining local-first data handling with real-time synchronization, RxDB delivers most of what an Optimistic UI needs - right out of the box. The result is a seamless user experience where interactions never feel blocked by slow networks, and any conflicts or final validations are quietly handled in the background.
-
-
-
-#### Handling Offline Changes and Conflicts
-- **Offline-first approach**: All writes are initially stored in the local database. When connectivity returns, RxDB's replication automatically pushes changes to the server.
-- **Conflict resolution**: If multiple clients edit the same documents while offline, conflicts are automatically detected and can be resolved gracefully (more on conflicts below).
-
-#### WebSockets, SSE, or Beyond
-
-For truly real-time communication - where server-confirmed changes instantly reach all clients - you can go beyond simple HTTP polling. Use WebSockets, Server-Sent Events (SSE), or other streaming protocols to broadcast updates the moment they occur. This pattern excels in scenarios like chats, collaborative editors, or dynamic dashboards.
-
-To learn more about these protocols and their integration with RxDB, check out [this guide](./websockets-sse-polling-webrtc-webtransport.md).
-
-## Optimistic UI in Various Frameworks
-
-### Angular Example
-
-
-
-
-[Angular](./angular-database.md)'s `async` pipe works smoothly with RxDB's observables. Suppose you have a `myCollection` of documents, you can directly subscribe in the template:
-
-```html
-
-
- {{ doc.name }}
-
-
-```
-This snippet:
-
-- Subscribes to `myCollection.find().$`, which emits live updates whenever [documents](../rx-document.md) in the [collection](../rx-collection.md) change.
-- Passes the emitted array of documents into docs.
-- Renders each document in a list item, instantly reflecting any changes.
-
-### React Example
-
-
-
-
-In [React](./react-database.md), you can utilize signals or other state management tools. For instance, if we have an [RxDB extension](../reactivity.md) that exposes a **signal**:
-
-```tsx
-import React from 'react';
-
-function MyComponent({ myCollection }) {
- // .find().$$ provides a signal that updates whenever data changes
- const docsSignal = myCollection.find().$$;
-
- return (
-
- {docs.map((doc) => (
- {doc.name}
- ))}
-
- );
-}
-
-export default MyComponent;
-```
-
-When you call `docsSignal.value` or use a hook like useSignal, it pulls the latest value from the [RxDB query](../rx-query.md). Whenever the collection updates, the signal emits the new data, and React re-renders the component instantly.
-
-## Downsides of Optimistic UI Apps
-
-While Optimistic UIs feel snappy, there are some caveats:
-
-- **Conflict Resolution**:
-With an optimistic approach, multiple offline devices might edit the same data. When syncing back, conflicts occur that must be merged. RxDB uses [revisions](../transactions-conflicts-revisions.md) to detect and handle these conflicts.
-
-- **User Confusion**:
-Users may see changes that haven't yet been confirmed by the server. If a subsequent server validation fails, the UI must revert to a previous state. Clear visual feedback or user notifications help reduce confusion.
-
-- **Server Compatibility**:
-The server must be capable of storing and returning revision metadata (for instance, a timestamp or versioning system). Check out RxDB's [replication docs](../replication.md) for details on how to structure your back end.
-
-- **Storage Limits**:
-Storing data in the client has practical [size limits](./indexeddb-max-storage-limit.md). [IndexedDB](../rx-storage-indexeddb.md) or other client-side storages have constraints (though usually quite large). See [storage comparisons](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md).
-
-## Conflict Resolution Strategies
-- **Last Write to Server Wins**:
-A simplest-possible method: whatever update reaches the server last overrides previous data. Good for non-critical data like “like" counts or ephemeral states.
-- **Revision-Based Merges**:
-Use revision numbers or timestamps to track concurrent edits. Merge them intelligently by combining fields or choosing the latest sub-document changes. This is ideal for collaborative apps where you don't want to overwrite entire records.
-- **User Prompts**:
-In certain workflows (e.g., shipping forms, e-commerce checkout), you may need to notify the user about conflicts and let them choose which version to keep.
-- **First Write to Server Wins (RxDB Default)**:
-RxDB's default approach is to let the first successful push define the latest version. Any incoming push with an outdated revision triggers a conflict that must be resolved on the client side. Learn more at [here](../transactions-conflicts-revisions.md).
-
-## When (and When Not) to Use Optimistic UI
-- When to Use
- - [Real-time interactions](./realtime-database.md) like chat apps, social feeds, or “Likes."
-Situations where high success rates of operations are expected (most writes don't fail).
- - Apps that need an [offline-first approach](../offline-first.md) or handle intermittent connectivity gracefully.
-
-- When Not to Use
- - Large, complex transactions with high failure rates.
- - Scenarios requiring heavy server validations or approvals (for example, financial transactions with complex rules).
- - Workflows where immediate feedback could mislead users about an operation's success probability.
-
-- Assessing Risk
- - Consider the likelihood that a user's action might fail. If it's very low, optimistic UI is often best.
- - If frequent failures or complex validations occur, consider a hybrid approach: partial optimistic updates for some actions, while more critical operations rely on immediate server confirmation.
-
-## Follow Up
-
-Ready to start building your own Optimistic UI with RxDB? Here are some next steps:
-
-1. **Do the [RxDB Quickstart](https://rxdb.info/quickstart.html)**
- If you're brand new to RxDB, the quickstart guide will walk you through installation and setting up your first project.
-
-2. **Check Out the Demo App**
- A live [RxDB Quickstart Demo](https://pubkey.github.io/rxdb-quickstart/) showcases optimistic updates and real-time syncing. Explore the code to see how it works.
-
-3. **Star the GitHub Repo**
- Show your support for RxDB by starring the [RxDB GitHub Repository](https://github.com/pubkey/rxdb).
-
-By combining RxDB's powerful offline-first capabilities with the principles of an Optimistic UI, you can deliver snappy, near-instant user interactions that keep your users engaged - no matter the network conditions. Get started today and give your users the experience they deserve!
diff --git a/docs/articles/progressive-web-app-database.html b/docs/articles/progressive-web-app-database.html
deleted file mode 100644
index 4991a74c7f8..00000000000
--- a/docs/articles/progressive-web-app-database.html
+++ /dev/null
@@ -1,109 +0,0 @@
-
-
-
-
-
-RxDB as a Database for Progressive Web Apps (PWA) | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Progressive Web Apps (PWAs) have revolutionized the digital landscape, offering users an immersive blend of web and native app experiences. At the heart of every successful PWA lies effective data management, and this is where RxDB comes into play. In this article, we'll explore the dynamic synergy between RxDB, a robust client-side database, and Progressive Web Apps, uncovering how RxDB enhances data handling, synchronization, and overall performance, propelling PWAs into a new era of excellence.
Progressive Web Apps are the future of web development, seamlessly combining the best of both web and mobile app worlds. They can be easily installed on the user's home screen, function offline, and load at lightning speed. Unlike hybrid apps, PWAs offer a consistent user experience across platforms, making them a versatile choice for modern applications.
-
PWAs bring a plethora of advantages to the table. They eliminate the hassle of app store installations and updates, reduce dependency on network connectivity, and prioritize fast loading times. By harnessing the power of service workers and intelligent caching mechanisms, PWAs ensure users can access content even in offline mode. Furthermore, PWAs are device-agnostic, seamlessly adapting to various devices, from desktops to smartphones.
-
Introducing RxDB as a Client-Side Database for PWAs
-
At the heart of PWAs lies efficient data management, and RxDB steps in as a reliable ally. As a client-side NoSQL database, RxDB seamlessly integrates into web applications, offering real-time data synchronization and manipulation capabilities. This article sheds light on the transformative potential of RxDB as it collaborates harmoniously with PWAs, enabling local-first strategies and elevating user interactions to a whole new level.
RxDB emerges as a reactive, schema-based NoSQL database crafted explicitly for client-side applications. Its real-time data synchronization and responsiveness align seamlessly with the dynamic demands of modern PWAs.
The cornerstone of RxDB's philosophy is the local-first approach, empowering PWAs to prioritize data storage and manipulation on the client side. This paradigm ensures that PWAs remain functional even when offline, allowing users to access and interact with data seamlessly. RxDB bridges any gaps in data synchronization once network connectivity is restored.
Observable queries (aka Live-Queries) serve as the engine of RxDB's dynamic capabilities. By leveraging these queries, PWAs can monitor and respond to data changes in real time. The result is an engaging user interface with instantaneous updates that captivate users and keep them engaged.
-
await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
-})
-.$ // the $ returns an observable that emits each time the result set of the query changes
-.subscribe(aliveHeroes => console.dir(aliveHeroes));
RxDB extends its prowess to multi-tab scenarios, guaranteeing data consistency across different tabs or windows of the same PWA. This feature promotes a seamless transition between various sections of the application, while minimizing data conflicts.
Integrating RxDB into a Progressive Web App, driven by technologies like React, is a straightforward process. By configuring RxDB and installing the necessary packages, developers establish a solid foundation for robust data management within their PWA.
RxDB caters to diverse needs through its various RxStorage layers:
-
-
localstorage RxStorage: Leveraging the capabilities of the browsers localstorage API for storage.
-
IndexedDB RxStorage: Tapping into the browser's IndexedDB for efficient data storage.
-
OPFS RxStorage: Interfacing with the Offline-First Persistence System for seamless persistence.
-
Memory RxStorage: Storing data in memory, ideal for temporary data requirements.
-This flexibility empowers developers to optimize data storage based on the unique needs of their PWA.
-
-
Synchronizing Data with RxDB between PWA Clients and Servers
-To facilitate seamless data synchronization between PWA clients and servers, RxDB offers a range of replication options:
-
-
-
RxDB Replication Algorithm: RxDB introduces its own replication algorithm, enabling efficient and reliable data synchronization between clients and servers.
-
-
-
CouchDB Replication: Leveraging its roots in CouchDB, RxDB facilitates smooth data replication between clients and CouchDB servers, ensuring data consistency and synchronization across devices.
-
-
-
Firestore Replication: RxDB synchronizes data with Google Firestore, a real-time cloud-hosted NoSQL database. This integration guarantees up-to-date data across different instances of the PWA.
-
-
-
Peer-to-Peer (P2P) via WebRTC Replication: RxDB supports P2P replication, facilitating direct data synchronization between clients without intermediaries. This decentralized approach is invaluable in scenarios where server infrastructure is limited.
RxDB empowers PWAs with the ability to encrypt local data, enhancing data security and safeguarding sensitive information. This feature is indispensable for applications handling user credentials, financial transactions, and other confidential data.
Performance optimization is a top priority for PWAs. RxDB addresses this concern by offering indexing options that expedite data retrieval, resulting in a snappier user interface and heightened responsiveness.
RxDB introduces JSON key compression, a feature that reduces storage requirements. This optimization is particularly beneficial for PWAs dealing with substantial data volumes, enhancing overall efficiency and resource utilization.
RxDB introduces change streams, enabling PWAs to react to data changes in real time. This capability empowers dynamic updates to the user interface, promoting interactivity and engagement.
In the ever-evolving landscape of web application development, Progressive Web Apps continue to redefine user experiences. RxDB emerges as a pivotal player, seamlessly integrating with PWAs and enhancing their capabilities. With features like the local-first approach, observable queries, replication mechanisms, and advanced encryption, RxDB empowers developers to create responsive, offline-capable, and data-driven PWAs. As the demand for sophisticated PWAs continues to surge, RxDB remains an indispensable tool for developers aiming to push the boundaries of innovation and redefine the standards of user engagement. By embracing RxDB, developers ensure their PWAs remain at the forefront of the digital revolution, offering seamless and immersive experiences to users around the world.
To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-
-
\ No newline at end of file
diff --git a/docs/articles/progressive-web-app-database.md b/docs/articles/progressive-web-app-database.md
deleted file mode 100644
index 021eb679158..00000000000
--- a/docs/articles/progressive-web-app-database.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# RxDB as a Database for Progressive Web Apps (PWA)
-
-> Discover how RxDB supercharges Progressive Web Apps with real-time sync, offline-first capabilities, and lightning-fast data handling.
-
-# RxDB as a Database for Progressive Web Apps (PWA)
-Progressive Web Apps (PWAs) have revolutionized the digital landscape, offering users an immersive blend of web and native app experiences. At the heart of every successful PWA lies effective data management, and this is where RxDB comes into play. In this article, we'll explore the dynamic synergy between RxDB, a robust client-side database, and Progressive Web Apps, uncovering how RxDB enhances data handling, synchronization, and overall performance, propelling PWAs into a new era of excellence.
-
-## What is a Progressive Web App
-Progressive Web Apps are the future of web development, seamlessly combining the best of both web and mobile app worlds. They can be easily installed on the user's home screen, function offline, and load at lightning speed. Unlike hybrid apps, PWAs offer a consistent user experience across platforms, making them a versatile choice for modern applications.
-
-PWAs bring a plethora of advantages to the table. They eliminate the hassle of app store installations and updates, reduce dependency on network connectivity, and prioritize fast loading times. By harnessing the power of service workers and intelligent caching mechanisms, PWAs ensure users can access content even in offline mode. Furthermore, PWAs are device-agnostic, seamlessly adapting to various devices, from desktops to smartphones.
-
-## Introducing RxDB as a Client-Side Database for PWAs
-At the heart of PWAs lies efficient data management, and RxDB steps in as a reliable ally. As a client-side NoSQL database, RxDB seamlessly integrates into web applications, offering real-time data synchronization and manipulation capabilities. This article sheds light on the transformative potential of RxDB as it collaborates harmoniously with PWAs, enabling local-first strategies and elevating user interactions to a whole new level.
-
-
-
-
-
-
-
-### Getting Started with RxDB
-RxDB emerges as a reactive, schema-based NoSQL database crafted explicitly for client-side applications. Its real-time data synchronization and responsiveness align seamlessly with the dynamic demands of modern PWAs.
-
-#### Local-First Approach
-The cornerstone of RxDB's philosophy is the local-first approach, empowering PWAs to prioritize data storage and manipulation on the client side. This paradigm ensures that PWAs remain functional even when offline, allowing users to access and interact with data seamlessly. RxDB bridges any gaps in data synchronization once network connectivity is restored.
-
-#### Observable Queries
-Observable queries (aka **Live-Queries**) serve as the engine of RxDB's dynamic capabilities. By leveraging these queries, PWAs can monitor and respond to data changes in real time. The result is an engaging user interface with instantaneous updates that captivate users and keep them engaged.
-
-```ts
-await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
-})
-.$ // the $ returns an observable that emits each time the result set of the query changes
-.subscribe(aliveHeroes => console.dir(aliveHeroes));
-```
-
-#### Multi-Tab Support
-RxDB extends its prowess to multi-tab scenarios, guaranteeing data consistency across different tabs or windows of the same PWA. This feature promotes a seamless transition between various sections of the application, while minimizing data conflicts.
-
-
-
-### Using RxDB in a Progressive Web App
-Integrating RxDB into a Progressive Web App, driven by technologies like React, is a straightforward process. By configuring RxDB and installing the necessary packages, developers establish a solid foundation for robust data management within their PWA.
-
-## Exploring Different RxStorage Layers
-RxDB caters to diverse needs through its various RxStorage layers:
-
-- [localstorage RxStorage](../rx-storage-localstorage.md): Leveraging the capabilities of the browsers localstorage API for storage.
-- [IndexedDB RxStorage](../rx-storage-indexeddb.md): Tapping into the browser's IndexedDB for efficient data storage.
-- [OPFS RxStorage](../rx-storage-opfs.md): Interfacing with the Offline-First Persistence System for seamless persistence.
-- [Memory RxStorage](../rx-storage-memory.md): Storing data in memory, ideal for temporary data requirements.
-This flexibility empowers developers to optimize data storage based on the unique needs of their PWA.
-
-Synchronizing Data with RxDB between PWA Clients and Servers
-To facilitate seamless data synchronization between PWA clients and servers, RxDB offers a range of replication options:
-
-- [RxDB Replication Algorithm](../replication.md): RxDB introduces its own replication algorithm, enabling efficient and reliable data synchronization between clients and servers.
-
-- [CouchDB Replication](../replication-couchdb.md): Leveraging its roots in CouchDB, RxDB facilitates smooth data replication between clients and CouchDB servers, ensuring data consistency and synchronization across devices.
-
-- [Firestore Replication](../replication-firestore.md): RxDB synchronizes data with Google Firestore, a real-time cloud-hosted NoSQL database. This integration guarantees up-to-date data across different instances of the PWA.
-
-- [Peer-to-Peer (P2P) via WebRTC](../replication-webrtc.md) Replication: RxDB supports P2P replication, facilitating direct data synchronization between clients without intermediaries. This decentralized approach is invaluable in scenarios where server infrastructure is limited.
-
-## Advanced RxDB Features and Techniques
-### Encryption of Local Data
-RxDB empowers PWAs with the ability to encrypt local data, enhancing data security and safeguarding sensitive information. This feature is indispensable for applications handling user credentials, financial transactions, and other confidential data.
-
-### Indexing and Performance Optimization
-Performance optimization is a top priority for PWAs. RxDB addresses this concern by offering indexing options that expedite data retrieval, resulting in a snappier user interface and heightened responsiveness.
-
-### JSON Key Compression
-RxDB introduces JSON key compression, a feature that reduces storage requirements. This optimization is particularly beneficial for PWAs dealing with substantial data volumes, enhancing overall efficiency and resource utilization.
-
-### Change Streams and Event Handling
-RxDB introduces change streams, enabling PWAs to react to data changes in real time. This capability empowers dynamic updates to the user interface, promoting interactivity and engagement.
-
-## Conclusion
-In the ever-evolving landscape of web application development, Progressive Web Apps continue to redefine user experiences. RxDB emerges as a pivotal player, seamlessly integrating with PWAs and enhancing their capabilities. With features like the local-first approach, observable queries, replication mechanisms, and advanced encryption, RxDB empowers developers to create responsive, offline-capable, and data-driven PWAs. As the demand for sophisticated PWAs continues to surge, RxDB remains an indispensable tool for developers aiming to push the boundaries of innovation and redefine the standards of user engagement. By embracing RxDB, developers ensure their PWAs remain at the forefront of the digital revolution, offering seamless and immersive experiences to users around the world.
-
-## Follow Up
-To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-- [RxDB Progressive Web App in Angular Example](https://github.com/pubkey/rxdb/tree/master/examples/angular)
diff --git a/docs/articles/react-database.html b/docs/articles/react-database.html
deleted file mode 100644
index fd21226cff2..00000000000
--- a/docs/articles/react-database.html
+++ /dev/null
@@ -1,149 +0,0 @@
-
-
-
-
-
-RxDB as a Database for React Applications | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In the rapidly evolving landscape of web development, React has emerged as a cornerstone technology for building dynamic and responsive user interfaces. With the increasing complexity of modern web applications, efficient data management becomes pivotal. This article delves into the integration of RxDB, a potent client-side database, with React applications to optimize data handling and elevate the overall user experience.
-
React has revolutionized the way web applications are built by introducing a component-based architecture. This approach enables developers to create reusable UI components that efficiently update in response to changes in data. The virtual DOM mechanism, a key feature of React, facilitates optimized rendering, enhancing performance and user interactivity.
-
While React excels at managing the user interface, the need for efficient data storage and retrieval mechanisms is equally significant. A client-side database brings several advantages to React applications:
-
-
Improved Performance: Local data storage reduces the need for frequent server requests, resulting in faster data retrieval and enhanced application responsiveness.
-
Offline Capabilities: A client-side database enables offline access to data, allowing users to interact with the application even when they are disconnected from the internet.
-
Real-Time Updates: With the ability to observe changes in data, client-side databases facilitate real-time updates to the UI, ensuring users are always presented with the latest information.
-
Reduced Server Load: By handling data operations locally, client-side databases alleviate the load on the server, contributing to a more scalable architecture.
RxDB, a powerful JavaScript database, has garnered attention as an optimal solution for managing data in React applications. Built on top of the IndexedDB standard, RxDB combines the principles of reactive programming with database management. Its core features include reactive data handling, offline-first capabilities, and robust data replication.
RxDB, short for Reactive Database, is an open-source JavaScript database that seamlessly integrates reactive programming with database operations. It offers a comprehensive API for performing database actions and synchronizing data across clients and servers. RxDB's underlying philosophy revolves around observables, allowing developers to reactively manage data changes and create dynamic user interfaces.
One of RxDB's standout features is its support for reactive data handling. Traditional databases often require manual intervention for data fetching and updating, leading to complex and error-prone code. RxDB, however, automatically notifies subscribers whenever data changes occur, eliminating the need for explicit data manipulation. This reactive approach simplifies code and enhances the responsiveness of React components.
RxDB embraces a local-first methodology, enabling applications to function seamlessly even in offline scenarios. By storing data locally, RxDB ensures that users can interact with the application and make updates regardless of internet connectivity. Once the connection is reestablished, RxDB synchronizes the local changes with the remote database, maintaining data consistency across devices.
Data replication is a cornerstone of modern applications that require synchronization between multiple clients and servers. RxDB provides robust data replication mechanisms that facilitate real-time synchronization between different instances of the database. This ensures that changes made on one client are promptly propagated to others, contributing to a cohesive and unified user experience.
RxDB extends the concept of observables beyond data changes. It introduces observable queries, allowing developers to observe the results of database queries. This feature enables automatic updates to query results whenever relevant data changes occur. Observable queries simplify state management by eliminating the need to manually trigger updates in response to changing data.
-
await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
-})
-.$ // the $ returns an observable that emits each time the result set of the query changes
-.subscribe(aliveHeroes => console.dir(aliveHeroes));
Web applications often operate in multiple browser tabs or windows. RxDB accommodates this scenario by offering built-in multi-tab support. It ensures that data changes made in one tab are efficiently propagated to other tabs, maintaining data consistency and providing a seamless experience for users interacting with the application across different tabs.
While considering database options for React applications, RxDB stands out due to its unique combination of reactive programming and database capabilities. Unlike traditional solutions such as IndexedDB or Web Storage, which provide basic data storage, RxDB offers a dedicated database solution with advanced features. Additionally, while state management libraries like Redux and MobX can be adapted for database use, RxDB provides an integrated solution specifically designed for handling data.
Using IndexedDB directly in React can be challenging due to its low-level, callback-based API which doesn't align neatly with modern React's Promise and async/await patterns. This intricacy often leads to bulky and complex implementations for developers. Also, when used wrong, IndexedDB can have a worse performance profile then it could have. In contrast, RxDB, with the IndexedDB RxStorage and the LocalStorage RxStorage, abstracts these complexities, integrating reactive programming and providing a more streamlined experience for data management in React applications. Thus, RxDB offers a more intuitive approach, eliminating much of the manual overhead required with IndexedDB.
The process of integrating RxDB into a React application is straightforward. Begin by installing RxDB as a dependency:
-npm install rxdb rxjs
-Once installed, RxDB can be imported and initialized within your React components. The following code snippet illustrates a basic setup:
The rxdb-hooks package provides a set of React hooks that simplify data management within components. These hooks leverage RxDB's reactivity to automatically update components when data changes occur. The following example demonstrates the usage of the useRxCollection and useRxQuery hooks to query and observe a collection:
RxDB offers multiple storage layers, each backed by a different underlying technology. Developers can choose the storage layer that best suits their application's requirements. Some available options include:
Memory RxStorage: Stores data in memory, primarily intended for testing and development purposes.
-
SQLite RxStorage: Stores data in an SQLite database. Can be used in a browser with react by using a SQLite database that was compiled to WebAssembly. Using SQLite in react might not be the best idea, because a compiled SQLite wasm file is about one megabyte of code that has to be loaded and rendered by your users. Using native browser APIs like IndexedDB and OPFS have shown to be a more optimal database solution for browser based react apps compared to SQLite.
-
-
Synchronizing Data with RxDB between Clients and Servers
-
The offline-first approach is a fundamental principle of RxDB's design. When dealing with client-server synchronization, RxDB ensures that changes made offline are captured and propagated to the server once connectivity is reestablished. This mechanism guarantees that data remains consistent across different client instances, even when operating in an occasionally connected environment.
-
RxDB offers a range of replication plugins that facilitate data synchronization between clients and servers. These plugins support various synchronization strategies, such as one-way replication, two-way replication, and custom conflict resolution. Developers can select the appropriate plugin based on their application's synchronization requirements.
Encryption of Local Data
-Security is paramount when handling sensitive user data. RxDB supports data encryption, ensuring that locally stored information remains protected from unauthorized access. This feature is particularly valuable when dealing with sensitive data in offline scenarios.
Efficient indexing is critical for achieving optimal database performance. RxDB provides mechanisms to define indexes on specific fields, enhancing query speed and reducing the computational overhead of data retrieval.
RxDB employs JSON key compression to reduce storage space and improve performance. This technique minimizes the memory footprint of the database, making it suitable for applications with limited resources.
RxDB enables developers to subscribe to change streams, which emit events whenever data changes occur. This functionality facilitates real-time event handling and provides opportunities for implementing features such as notifications and live updates.
In the realm of React application development, efficient data management is pivotal to delivering a seamless and engaging user experience. RxDB emerges as a compelling solution, seamlessly integrating reactive programming principles with sophisticated database capabilities. By adopting RxDB, React developers can harness its powerful features, including reactive data handling, offline-first support, and real-time synchronization. With RxDB as a foundational pillar, React applications can excel in responsiveness, scalability, and data integrity. As the landscape of web development continues to evolve, RxDB remains a steadfast companion for creating robust and dynamic React applications.
To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-
-
\ No newline at end of file
diff --git a/docs/articles/react-database.md b/docs/articles/react-database.md
deleted file mode 100644
index bde5d41b8d1..00000000000
--- a/docs/articles/react-database.md
+++ /dev/null
@@ -1,152 +0,0 @@
-# RxDB as a Database for React Applications
-
-> earn how the RxDB database supercharges React apps with offline access, real-time updates, and smooth data flow. Boost performance and engagement.
-
-# RxDB as a Database for React Applications
-In the rapidly evolving landscape of web development, React has emerged as a cornerstone technology for building dynamic and responsive user interfaces. With the increasing complexity of modern web applications, efficient data management becomes pivotal. This article delves into the integration of RxDB, a potent client-side database, with React applications to optimize data handling and elevate the overall user experience.
-
-React has revolutionized the way web applications are built by introducing a component-based architecture. This approach enables developers to create reusable UI components that efficiently update in response to changes in data. The virtual DOM mechanism, a key feature of React, facilitates optimized rendering, enhancing performance and user interactivity.
-
-While React excels at managing the user interface, the need for efficient data storage and retrieval mechanisms is equally significant. A client-side database brings several advantages to React applications:
-
-- Improved Performance: Local data storage reduces the need for frequent server requests, resulting in faster data retrieval and enhanced application responsiveness.
-- Offline Capabilities: A client-side database enables offline access to data, allowing users to interact with the application even when they are disconnected from the internet.
-- Real-Time Updates: With the ability to observe changes in data, client-side databases facilitate real-time updates to the UI, ensuring users are always presented with the latest information.
-- Reduced Server Load: By handling data operations locally, client-side databases alleviate the load on the server, contributing to a more scalable architecture.
-
-## Introducing RxDB as a JavaScript Database
-RxDB, a powerful JavaScript database, has garnered attention as an optimal solution for managing data in React applications. Built on top of the IndexedDB standard, RxDB combines the principles of reactive programming with database management. Its core features include reactive data handling, offline-first capabilities, and robust data replication.
-
-
-
-
-
-
-
-## What is RxDB?
-RxDB, short for Reactive Database, is an open-source JavaScript database that seamlessly integrates reactive programming with database operations. It offers a comprehensive API for performing database actions and synchronizing data across clients and servers. RxDB's underlying philosophy revolves around observables, allowing developers to reactively manage data changes and create dynamic user interfaces.
-
-### Reactive Data Handling
-One of RxDB's standout features is its support for reactive data handling. Traditional databases often require manual intervention for data fetching and updating, leading to complex and error-prone code. RxDB, however, automatically notifies subscribers whenever data changes occur, eliminating the need for explicit data manipulation. This reactive approach simplifies code and enhances the responsiveness of React components.
-
-### Local-First Approach
-RxDB embraces a [local-first](../offline-first.md) methodology, enabling applications to function seamlessly even in offline scenarios. By storing data locally, RxDB ensures that users can interact with the application and make updates regardless of internet connectivity. Once the connection is reestablished, RxDB synchronizes the local changes with the remote database, maintaining data consistency across devices.
-
-### Data Replication
-Data replication is a cornerstone of modern applications that require synchronization between multiple clients and servers. RxDB provides robust data replication mechanisms that facilitate real-time synchronization between different instances of the database. This ensures that changes made on one client are promptly propagated to others, contributing to a cohesive and unified user experience.
-
-### Observable Queries
-RxDB extends the concept of observables beyond data changes. It introduces observable queries, allowing developers to observe the results of database queries. This feature enables automatic updates to query results whenever relevant data changes occur. Observable queries simplify state management by eliminating the need to manually trigger updates in response to changing data.
-
-```ts
-await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
-})
-.$ // the $ returns an observable that emits each time the result set of the query changes
-.subscribe(aliveHeroes => console.dir(aliveHeroes));
-```
-
-### Multi-Tab Support
-Web applications often operate in multiple browser tabs or windows. RxDB accommodates this scenario by offering built-in multi-tab support. It ensures that data changes made in one tab are efficiently propagated to other tabs, maintaining data consistency and providing a seamless experience for users interacting with the application across different tabs.
-
-
-
-### RxDB vs. Other React Database Options
-While considering database options for React applications, RxDB stands out due to its unique combination of reactive programming and database capabilities. Unlike traditional solutions such as IndexedDB or Web Storage, which provide basic data storage, RxDB offers a dedicated database solution with advanced features. Additionally, while state management libraries like Redux and MobX can be adapted for database use, RxDB provides an integrated solution specifically designed for handling data.
-
-### IndexedDB in React and the Advantage of RxDB
-
-Using IndexedDB directly in React can be challenging due to its low-level, callback-based API which doesn't align neatly with modern React's Promise and async/await patterns. This intricacy often leads to bulky and complex implementations for developers. Also, when used wrong, IndexedDB can have a worse [performance profile](../slow-indexeddb.md) then it could have. In contrast, RxDB, with the [IndexedDB RxStorage](../rx-storage-indexeddb.md) and the [LocalStorage RxStorage](../rx-storage-localstorage.md), abstracts these complexities, integrating reactive programming and providing a more streamlined experience for data management in React applications. Thus, RxDB offers a more intuitive approach, eliminating much of the manual overhead required with IndexedDB.
-
-### Using RxDB in a React Application
-
-The process of integrating RxDB into a React application is straightforward. Begin by installing RxDB as a dependency:
-`npm install rxdb rxjs`
-Once installed, RxDB can be imported and initialized within your React components. The following code snippet illustrates a basic setup:
-
-```javascript
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-const db = await createRxDatabase({
- name: 'heroesdb', // <- name
- storage: getRxStorageLocalstorage(), // <- RxStorage
- password: 'myPassword', // <- password (optional)
- multiInstance: true, // <- multiInstance (optional, default: true)
- eventReduce: true, // <- eventReduce (optional, default: false)
- cleanupPolicy: {} // <- custom cleanup policy (optional)
-});
-```
-
-### Using RxDB React Hooks
-The [rxdb-hooks](https://github.com/cvara/rxdb-hooks) package provides a set of React hooks that simplify data management within components. These hooks leverage RxDB's reactivity to automatically update components when data changes occur. The following example demonstrates the usage of the `useRxCollection` and `useRxQuery` hooks to query and observe a collection:
-
-```ts
-const collection = useRxCollection('characters');
-const query = collection.find().where('affiliation').equals('Jedi');
-const {
- result: characters,
- isFetching,
- fetchMore,
- isExhausted,
-} = useRxQuery(query, {
- pageSize: 5,
- pagination: 'Infinite',
-});
-
-if (isFetching) {
- return 'Loading...';
-}
-
-return (
-
- {characters.map((character, index) => (
-
- ))}
- {!isExhausted && }
-
-);
-```
-
-### Different RxStorage Layers for RxDB
-RxDB offers multiple storage layers, each backed by a different underlying technology. Developers can choose the storage layer that best suits their application's requirements. Some available options include:
-
-- [LocalStorage RxStorage](../rx-storage-localstorage.md): Built on top of the browsers localstorage API.
-- [IndexedDB RxStorage](../rx-storage-indexeddb.md): The default RxDB storage layer, providing efficient data storage in modern browsers.
-- [OPFS RxStorage](../rx-storage-opfs.md): Uses the Operational File System (OPFS) for storage, suitable for [Electron applications](../electron-database.md).
-- [Memory RxStorage](../rx-storage-memory.md): Stores data in memory, primarily intended for testing and development purposes.
-- [SQLite RxStorage](../rx-storage-sqlite.md): Stores data in an SQLite database. Can be used in a browser with react by using a SQLite database that was [compiled to WebAssembly](https://sqlite.org/wasm/doc/trunk/index.md). Using SQLite in react might not be the best idea, because a compiled SQLite wasm file is about one megabyte of code that has to be loaded and rendered by your users. Using native browser APIs like IndexedDB and OPFS have shown to be a more optimal database solution for browser based react apps compared to SQLite.
-
-### Synchronizing Data with RxDB between Clients and Servers
-The offline-first approach is a fundamental principle of RxDB's design. When dealing with client-server synchronization, RxDB ensures that changes made offline are captured and propagated to the server once connectivity is reestablished. This mechanism guarantees that data remains consistent across different client instances, even when operating in an occasionally connected environment.
-
-RxDB offers a range of [replication plugins](../replication.md) that facilitate data synchronization between clients and servers. These plugins support various synchronization strategies, such as one-way replication, two-way replication, and custom conflict resolution. Developers can select the appropriate plugin based on their application's synchronization requirements.
-
-
-
-### Advanced RxDB Features and Techniques
-Encryption of Local Data
-Security is paramount when handling sensitive user data. RxDB supports [data encryption](./react-native-encryption.md), ensuring that locally stored information remains protected from unauthorized access. This feature is particularly valuable when dealing with sensitive data in offline scenarios.
-
-### Indexing and Performance Optimization
-Efficient indexing is critical for achieving optimal database performance. RxDB provides mechanisms to define indexes on specific fields, enhancing query speed and reducing the computational overhead of data retrieval.
-
-### JSON Key Compression
-RxDB employs JSON key compression to reduce storage space and improve performance. This technique minimizes the memory footprint of the database, making it suitable for applications with limited resources.
-
-### Change Streams and Event Handling
-RxDB enables developers to subscribe to change streams, which emit events whenever data changes occur. This functionality facilitates real-time event handling and provides opportunities for implementing features such as notifications and live updates.
-
-## Conclusion
-In the realm of React application development, efficient data management is pivotal to delivering a seamless and engaging user experience. RxDB emerges as a compelling solution, seamlessly integrating reactive programming principles with sophisticated database capabilities. By adopting RxDB, React developers can harness its powerful features, including reactive data handling, offline-first support, and real-time synchronization. With RxDB as a foundational pillar, React applications can excel in responsiveness, scalability, and data integrity. As the landscape of web development continues to evolve, RxDB remains a steadfast companion for creating robust and dynamic React applications.
-
-## Follow Up
-To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-- [RxDB GitHub Repository](https://github.com/pubkey/rxdb): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects.
-- [RxDB React Example at GitHub](https://github.com/pubkey/rxdb/tree/master/examples/react)
diff --git a/docs/articles/react-indexeddb.html b/docs/articles/react-indexeddb.html
deleted file mode 100644
index 152d56660a1..00000000000
--- a/docs/articles/react-indexeddb.html
+++ /dev/null
@@ -1,225 +0,0 @@
-
-
-
-
-
-IndexedDB Database in React Apps - The Power of RxDB | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
IndexedDB Database in React Apps - The Power of RxDB
-
Building robust, offline-capable React applications often involves leveraging browser storage solutions to manage data. IndexedDB is one such powerful tool, but its raw API can be challenging to work with directly. RxDB abstracts away much of IndexedDB's complexity, providing a more developer-friendly experience. In this article, we'll explore what IndexedDB is, why it's beneficial in React applications, the challenges of using plain IndexedDB, and how RxDB can simplify your development process while adding advanced features.
IndexedDB is a low-level API for storing significant amounts of structured data in the browser. It provides a transactional database system that can store key-value pairs, complex objects, and more. This storage engine is asynchronous and supports advanced data types, making it suitable for offline storage and complex web applications.
When building React applications, IndexedDB can play a crucial role in enhancing both performance and user experience. Here are some reasons to consider using IndexedDB:
-
-
Offline-First / Local-First: By storing data locally, your application remains functional even without an internet connection.
-
Performance: Using local data means zero latency and no loading spinners, as data doesn't need to be fetched over a network.
-
Easier Implementation: Replicating all data to the client once is often simpler than implementing multiple endpoints for each user interaction.
-
Scalability: Local data reduces server load because queries run on the client side, decreasing server bandwidth and processing requirements.
While IndexedDB itself is powerful, its native API comes with several drawbacks for everyday application developers:
-
-
Callback-Based API: IndexedDB was designed with callbacks rather than modern Promises, making asynchronous code more cumbersome.
-
Complexity: IndexedDB is low-level, intended for library developers rather than for app developers who simply want to store data.
-
Basic Query API: Its rudimentary query capabilities limit how you can efficiently perform complex queries, whereas libraries like RxDB offer more advanced query features.
-
TypeScript Support: Ensuring good TypeScript support with IndexedDB is challenging, especially when trying to enforce document type consistency.
-
Lack of Observable API: IndexedDB doesn't provide an observable API out of the box. RxDB solves this by enabling you to observe query results or specific document fields.
-
Cross-Tab Communication: Managing cross-tab updates in plain IndexedDB is difficult. RxDB handles this seamlessly-changes in one tab automatically affect observed data in others.
-
Missing Advanced Features: Features like encryption or compression aren't built into IndexedDB, but they are available via RxDB.
-
Limited Platform Support: IndexedDB exists only in the browser. In contrast, RxDB offers swappable storages to use the same code in React Native, Capacitor, or Electron.
The premium plain IndexedDB-based storage, offering faster performance
-Below is an example of setting up a simple RxDB database using the localstorage-based storage in a React app:
-
-
import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-// create a database
-const db = await createRxDatabase({
- name: 'heroesdb', // the name of the database
- storage: getRxStorageLocalstorage()
-});
-
-// Define your schema
-const heroSchema = {
- title: 'hero schema',
- version: 0,
- description: 'Describes a hero in your app',
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- name: {
- type: 'string'
- },
- power: {
- type: 'string'
- }
- },
- required: ['id', 'name']
-};
-
-// add collections
-await db.addCollections({
- heroes: {
- schema: heroSchema
- }
-});
RxDB excels in providing reactive data capabilities, ideal for real-time applications. There are two main approaches to achieving live queries with RxDB: using RxJS Observables with React Hooks or utilizing Preact Signals.
RxDB integrates seamlessly with RxJS Observables, allowing you to build reactive components. Here's an example of a React component that subscribes to live data updates:
RxDB also supports Preact Signals for reactivity, which can be integrated into React applications. Preact Signals offer a modern, fine-grained reactivity model.
A comprehensive example of using RxDB within a React application can be found in the RxDB GitHub repository. This repository contains sample applications, showcasing best practices and demonstrating how to integrate RxDB for various use cases.
By leveraging RxDB on top of IndexedDB, you can create highly responsive, offline-capable React applications without dealing with the low-level complexities of IndexedDB directly. With reactive queries, seamless cross-tab communication, and powerful advanced features, RxDB becomes an invaluable tool in modern web development.
-
-
\ No newline at end of file
diff --git a/docs/articles/react-indexeddb.md b/docs/articles/react-indexeddb.md
deleted file mode 100644
index 8ac50a08823..00000000000
--- a/docs/articles/react-indexeddb.md
+++ /dev/null
@@ -1,247 +0,0 @@
-# IndexedDB Database in React Apps - The Power of RxDB
-
-> Discover how RxDB simplifies IndexedDB in React, offering reactive queries, offline-first capability, encryption, compression, and effortless integration.
-
-# IndexedDB Database in React Apps - The Power of RxDB
-
-Building robust, [offline-capable](../offline-first.md) React applications often involves leveraging browser storage solutions to manage data. IndexedDB is one such powerful tool, but its raw API can be challenging to work with directly. RxDB abstracts away much of IndexedDB's complexity, providing a more developer-friendly experience. In this article, we'll explore what IndexedDB is, why it's beneficial in React applications, the challenges of using plain IndexedDB, and how [RxDB](https://rxdb.info/) can simplify your development process while adding advanced features.
-
-## What is IndexedDB?
-
-[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) is a low-level API for storing significant amounts of structured data in the browser. It provides a transactional database system that can store key-value pairs, complex objects, and more. This storage engine is asynchronous and supports advanced data types, making it suitable for offline storage and complex web applications.
-
-
-
-
-
-## Why Use IndexedDB in React
-
-When building React applications, IndexedDB can play a crucial role in enhancing both performance and user experience. Here are some reasons to consider using IndexedDB:
-
-- **Offline-First / Local-First**: By storing data locally, your application remains functional even without an internet connection.
-- **Performance**: Using local data means [zero latency](./zero-latency-local-first.md) and no loading spinners, as data doesn't need to be fetched over a network.
-- **Easier Implementation**: Replicating all data to the client once is often simpler than implementing multiple endpoints for each user interaction.
-- **Scalability**: Local data reduces server load because queries run on the client side, decreasing server bandwidth and processing requirements.
-
-## Why To Not Use Plain IndexedDB
-
-While IndexedDB itself is powerful, its native API comes with several drawbacks for everyday application developers:
-
-- **Callback-Based API**: IndexedDB was designed with callbacks rather than modern Promises, making asynchronous code more cumbersome.
-- **Complexity**: IndexedDB is low-level, intended for library developers rather than for app developers who simply want to store data.
-- **Basic Query API**: Its rudimentary query capabilities limit how you can efficiently perform complex queries, whereas libraries like RxDB offer more advanced query features.
-- **TypeScript Support**: Ensuring good TypeScript support with IndexedDB is challenging, especially when trying to enforce document type consistency.
-- **Lack of Observable API**: IndexedDB doesn't provide an observable API out of the box. RxDB solves this by enabling you to observe query results or specific document fields.
-- **Cross-Tab Communication**: Managing cross-tab updates in plain IndexedDB is difficult. RxDB handles this seamlessly-changes in one tab automatically affect observed data in others.
-- **Missing Advanced Features**: Features like encryption or compression aren't built into IndexedDB, but they are available via RxDB.
-- **Limited Platform Support**: IndexedDB exists only in the browser. In contrast, RxDB offers swappable storages to use the same code in React Native, Capacitor, or Electron.
-
-
-
-
-
-
-
-## Set up RxDB in React
-
-Setting up RxDB with React is straightforward. It abstracts IndexedDB complexities and adds a layer of powerful features over it.
-
-### Installing RxDB
-
-First, install RxDB and RxJS from npm:
-
-```bash
-npm install rxdb rxjs --save```
-```
-
-### Create a Database and Collections
-
-RxDB provides two main storage options:
-- The free [localstorage-based storage](../rx-storage-localstorage.md)
-- The premium plain [IndexedDB-based storage](../rx-storage-indexeddb.md), offering faster performance
-Below is an example of setting up a simple RxDB [database](./react-database.md) using the localstorage-based storage in a React app:
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-// create a database
-const db = await createRxDatabase({
- name: 'heroesdb', // the name of the database
- storage: getRxStorageLocalstorage()
-});
-
-// Define your schema
-const heroSchema = {
- title: 'hero schema',
- version: 0,
- description: 'Describes a hero in your app',
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- name: {
- type: 'string'
- },
- power: {
- type: 'string'
- }
- },
- required: ['id', 'name']
-};
-
-// add collections
-await db.addCollections({
- heroes: {
- schema: heroSchema
- }
-});
-```
-
-### CRUD Operations
-
-Once your database is initialized, you can perform all CRUD operations:
-
-```ts
-// insert
-await db.heroes.insert({ name: 'Iron Man', power: 'Genius-level intellect' });
-
-// bulk insert
-await db.heroes.bulkInsert([
- { name: 'Thor', power: 'God of Thunder' },
- { name: 'Hulk', power: 'Superhuman Strength' }
-]);
-
-// find and findOne
-const heroes = await db.heroes.find().exec();
-const ironMan = await db.heroes.findOne({ selector: { name: 'Iron Man' } }).exec();
-
-// update
-const doc = await db.heroes.findOne({ selector: { name: 'Hulk' } }).exec();
-await doc.update({ $set: { power: 'Unlimited Strength' } });
-
-// delete
-const doc = await db.heroes.findOne({ selector: { name: 'Thor' } }).exec();
-await doc.remove();
-```
-
-## Reactive Queries and Live Updates
-
-RxDB excels in providing reactive data capabilities, ideal for [real-time applications](./realtime-database.md). There are two main approaches to achieving live queries with RxDB: using RxJS Observables with React Hooks or utilizing Preact Signals.
-
-
-
-### With RxJS Observables and React Hooks
-
-RxDB integrates seamlessly with RxJS Observables, allowing you to build reactive components. Here's an example of a React component that subscribes to live data updates:
-
-```ts
-import { useState, useEffect } from 'react';
-
-function HeroList({ collection }) {
- const [heroes, setHeroes] = useState([]);
-
- useEffect(() => {
- // create an observable query
- const query = collection.find();
- const subscription = query.$.subscribe(newHeroes => {
- setHeroes(newHeroes);
- });
- return () => subscription.unsubscribe();
- }, [collection]);
-
- return (
-
- Hero List
-
- {heroes.map(hero => (
-
- {hero.name} - {hero.power}
-
- ))}
-
-
- );
-}
-```
-
-This component subscribes to the collection's changes, updating the UI automatically whenever the underlying data changes, even across browser tabs.
-
-### With Preact Signals
-
-RxDB also supports Preact Signals for reactivity, which can be integrated into React applications. Preact Signals offer a modern, fine-grained reactivity model.
-
-First, install the necessary package:
-```bash
-npm install @preact/signals-core --save
-```
-Set up RxDB with Preact Signals reactivity:
-
-```ts
-import { PreactSignalsRxReactivityFactory } from 'rxdb/plugins/reactivity-preact-signals';
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-const database = await createRxDatabase({
- name: 'mydb',
- storage: getRxStorageLocalstorage(),
- reactivity: PreactSignalsRxReactivityFactory
-});
-```
-
-Now, you can obtain signals directly from RxDB queries using the double-dollar sign (`$$`):
-
-```ts
-function HeroList({ collection }) {
- const heroes = collection.find().$$;
- return (
-
- {heroes.map(hero => (
- {hero.name}
- ))}
-
- );
-}
-```
-
-This approach provides automatic updates whenever the data changes, without needing to manage subscriptions manually.
-
-## React IndexedDB Example with RxDB
-
-A comprehensive example of using RxDB within a React application can be found in the [RxDB GitHub repository](https://github.com/pubkey/rxdb/tree/master/examples/react). This repository contains sample applications, showcasing best practices and demonstrating how to integrate RxDB for various use cases.
-
-## Advanced RxDB Features
-
-RxDB offers many advanced features that extend beyond basic data storage:
-
-- **RxDB Replication**: Synchronize local data with remote databases seamlessly. Learn more: [RxDB Replication](https://rxdb.info/replication.html)
-- **Data Migration**: Handle schema changes gracefully with automatic data migrations. See: [Data migration](https://rxdb.info/migration-schema.html)
-- **Encryption**: Secure your data with built-in encryption capabilities. Explore: [Encryption](https://rxdb.info/encryption.html)
-- **Compression**: Optimize storage using key compression. Details: [Compression](https://rxdb.info/key-compression.html)
-
-## Limitations of IndexedDB
-
-While IndexedDB is powerful, it has some inherent limitations:
-
-- **Performance**: IndexedDB can be slow under certain conditions. Read more: [Slow IndexedDB](https://rxdb.info/slow-indexeddb.html)
-- **Storage Limits**: Browsers [impose limits](./indexeddb-max-storage-limit.md) on how much data can be stored. See: [Browser storage limits](https://rxdb.info/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html)
-
-## Alternatives to IndexedDB
-Depending on your application's requirements, there are [alternative storage solutions](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md) to consider:
-
-- **Origin Private File System (OPFS)**: A newer API that can offer better performance. RxDB supports OPFS as well. More info: [RxDB OPFS Storage](../rx-storage-opfs.md)
-- **SQLite**: Ideal for React applications on Capacitor or [Ionic](./ionic-storage.md), offering native performance. Explore: [RxDB SQLite Storage](../rx-storage-sqlite.md)
-
-## Performance comparison with other browser storages
-Here is a [performance overview](../rx-storage-performance.md) of the various browser based storage implementation of RxDB:
-
-
-
-## Follow Up
-- Learn how to use RxDB with the [RxDB Quickstart](../quickstart.md) for a guided introduction.
-- Check out the [RxDB GitHub repository](https://github.com/pubkey/rxdb) and leave a star ⭐ if you find it useful.
-
-By leveraging RxDB on top of IndexedDB, you can create highly responsive, offline-capable React applications without dealing with the low-level complexities of IndexedDB directly. With reactive queries, seamless cross-tab communication, and powerful advanced features, RxDB becomes an invaluable tool in modern web development.
diff --git a/docs/articles/react-native-encryption.html b/docs/articles/react-native-encryption.html
deleted file mode 100644
index 57a98b566fc..00000000000
--- a/docs/articles/react-native-encryption.html
+++ /dev/null
@@ -1,211 +0,0 @@
-
-
-
-
-
-React Native Encryption and Encrypted Database/Storage | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
React Native Encryption and Encrypted Database/Storage
-
Data security is a critical concern in modern mobile applications. As React Native continues to grow in popularity for building cross-platform apps, ensuring that your data is protected is paramount. RxDB, a real-time database for JavaScript applications, offers powerful encryption features that can help you secure your React Native app's data.
-
This article explains why encryption is important, how to set it up with RxDB in React Native, and best practices to keep your app secure.
Encryption ensures that, even if an unauthorized party obtains physical access to your device or intercepts data, they cannot read the information without the encryption key. Sensitive user data such as credentials, personal information, or financial details should always be encrypted. Proper encryption practices reduce the risk of data breaches and help your application remain compliant with regulations like GDPR or HIPAA.
Custom Encryption
-If you need more fine-grained control, you can integrate libraries like crypto-js or the Web Crypto API to encrypt data before storing it in a database or file.
CryptoJS Plugin: A free and straightforward solution for most basic use cases.
-
Web Crypto Plugin: A premium plugin that utilizes the native Web Crypto API for better performance and security.
-
-
Below is an example showing how to set up RxDB using the CryptoJS plugin. This example uses the in-memory storage for testing purposes. In a real production scenario, you would use a persistent storage adapter, mostly the SQLite-based storage.
-
import { createRxDatabase } from 'rxdb';
-import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js';
-
-/*
- * For testing, we use the in-memory storage of RxDB.
- * In production you would use the persistent SQLite based storage instead.
- */
-import { getRxStorageMemory } from 'rxdb/plugins/storage-memory';
-
-async function initEncryptedDatabase() {
- // Wrap the normal storage with the encryption plugin
- const encryptedMemoryStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageMemory()
- });
-
- // Create an encrypted database
- const db = await createRxDatabase({
- name: 'myEncryptedDatabase',
- storage: encryptedMemoryStorage,
- password: 'sudoLetMeIn' // Make sure not to hardcode in production
- });
-
- // Define a schema and create a collection
- await db.addCollections({
- secureData: {
- schema: {
- title: 'secure data schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- normalField: {
- type: 'string'
- },
- secretField: {
- type: 'string'
- }
- },
- required: ['id', 'normalField', 'secretField']
- }
- }
- });
-
- return db;
-}
Once you've set up the database with encryption, data in fields specified by your schema will be encrypted automatically before it is stored, and decrypted when queried.
-
(async () => {
- const db = await initEncryptedDatabase();
-
- // Insert encrypted data
- const doc = await db.secureData.insert({
- id: 'mySecretId',
- normalField: 'foobar',
- secretField: 'This is top secret data'
- });
-
- // Query encrypted data by its primary key or non-encrypted fields
- const fetchedDoc = await db.secureData.findOne({
- selector: {
- normalField: 'foobar'
- }
- }).exec(true);
- console.log(fetchedDoc.secretField); // 'This is top secret data'
-
- // Update data
- await fetchedDoc.patch({
- secretField: 'Updated secret data'
- });
-})();
-
Note: You can only query directly by non-encrypted fields or primary keys. Encrypted fields cannot be used in queries because they are stored as ciphertext in the database. A common approach is to have a small subset of fields that need to be queried unencrypted while storing any sensitive data in encrypted fields.
Use secure storage solutions like React Native Keychain or react-native-encrypted-storage to fetch the database password at runtime:
-
-
-
-
// Example: using react-native-keychain to securely retrieve a stored password
-import * as Keychain from 'react-native-keychain';
-
-async function getDatabasePassword() {
- const credentials = await Keychain.getGenericPassword();
- if (credentials) {
- return credentials.password;
- }
- throw new Error('No password stored in Keychain');
-}
-
-
Encrypt Attachments:
-If you need to store files (images, text files, etc.), consider encrypting attachments. RxDB supports attachments that can be encrypted automatically, ensuring your files are protected:
If performance is critical, consider using the premium Web Crypto plugin, which leverages native APIs for faster encryption and decryption.
-
If big chunks of data are encrypted, store them in attachments instead of document fields. Attachments will only be decrypted on explicit fetches, not during queries.
-
-
-
-
Use DevMode in Development: RxDB's DevMode Plugin can help validate your schema and encryption setup during development. Disable it in production for performance reasons.
-
-
-
Secure Communication:
-
-
Use HTTPS to secure network communication between the app and any backend services.
-
If you're synchronizing data to a server, ensure the data is also encrypted in transit. RxDB's replication plugins can work with secure endpoints to keep data consistent.
-
-
-
-
SSL Pinning: Consider SSL Pinning if you want to prevent man-in-the-middle attacks. SSL Pinning ensures the device trusts only the pinned certificate, preventing attackers from swapping out valid certificates with their own.
By following these best practices and leveraging RxDB's powerful encryption plugins, you can build secure, performant, and robust React Native applications that keep your users' data safe.
-
-
\ No newline at end of file
diff --git a/docs/articles/react-native-encryption.md b/docs/articles/react-native-encryption.md
deleted file mode 100644
index 4b87c89ee54..00000000000
--- a/docs/articles/react-native-encryption.md
+++ /dev/null
@@ -1,192 +0,0 @@
-# React Native Encryption and Encrypted Database/Storage
-
-> Secure your React Native app with RxDB encryption. Learn why it matters, how to implement encrypted databases, and best practices to protect user data.
-
-# React Native Encryption and Encrypted Database/Storage
-
-Data security is a critical concern in modern mobile applications. As React Native continues to grow in popularity for building cross-platform apps, ensuring that your data is protected is paramount. RxDB, a real-time database for JavaScript applications, offers powerful encryption features that can help you secure your React Native app's data.
-
-This article explains why encryption is important, how to set it up with RxDB in [React Native](../react-native-database.md), and best practices to keep your app secure.
-
-## 🔒 Why Encryption Matters
-
-Encryption ensures that, even if an unauthorized party obtains physical access to your device or intercepts data, they cannot read the information without the encryption key. Sensitive user data such as credentials, personal information, or financial details should always be encrypted. Proper encryption practices reduce the risk of data breaches and help your application remain compliant with regulations like [GDPR](https://gdpr.eu/) or [HIPAA](https://www.hhs.gov/hipaa/index.html).
-
-## React Native Encryption Overview
-
-React Native supports multiple ways to secure local data:
-
-1. **Encrypted Databases**
- Use databases with built-in encryption capabilities, such as SQLite with encryption layers or RxDB with its [encryption plugin](../encryption.md).
-
-2. **Secure Storage Libraries**
- For key-value data (like tokens or secrets), you can use libraries like [react-native-keychain](https://github.com/oblador/react-native-keychain) or [react-native-encrypted-storage](https://github.com/emeraldsanto/react-native-encrypted-storage).
-
-3. **Custom Encryption**
- If you need more fine-grained control, you can integrate libraries like [`crypto-js`](https://github.com/brix/crypto-js) or the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) to encrypt data before storing it in a database or file.
-
-
-
-
-
-
-
-## Setting Up Encryption in RxDB for React Native
-
-### 1. Install RxDB and Required Plugins
-
-Install RxDB and the encryption plugin(s) you need. For the CryptoJS plugin:
-
-```bash
-npm install rxdb
-npm install crypto-js
-```
-
-### 2. Set Up Your RxDB Database with Encryption
-
-RxDB offers two [encryption plugins](../encryption.md):
-- **CryptoJS Plugin**: A free and straightforward solution for most basic use cases.
-- **Web Crypto Plugin**: A [premium plugin](/premium) that utilizes the native Web Crypto API for better performance and security.
-
-Below is an example showing how to set up RxDB using the CryptoJS plugin. This example uses the [in-memory storage](../rx-storage-memory.md) for testing purposes. In a real production scenario, you would use a persistent storage adapter, mostly the [SQLite-based storage](../rx-storage-sqlite.md).
-
-```js
-import { createRxDatabase } from 'rxdb';
-import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js';
-
-/*
- * For testing, we use the in-memory storage of RxDB.
- * In production you would use the persistent SQLite based storage instead.
- */
-import { getRxStorageMemory } from 'rxdb/plugins/storage-memory';
-
-async function initEncryptedDatabase() {
- // Wrap the normal storage with the encryption plugin
- const encryptedMemoryStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageMemory()
- });
-
- // Create an encrypted database
- const db = await createRxDatabase({
- name: 'myEncryptedDatabase',
- storage: encryptedMemoryStorage,
- password: 'sudoLetMeIn' // Make sure not to hardcode in production
- });
-
- // Define a schema and create a collection
- await db.addCollections({
- secureData: {
- schema: {
- title: 'secure data schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- normalField: {
- type: 'string'
- },
- secretField: {
- type: 'string'
- }
- },
- required: ['id', 'normalField', 'secretField']
- }
- }
- });
-
- return db;
-}
-```
-
-### 3. Inserting and Querying Encrypted Data
-
-Once you've set up the database with encryption, data in fields specified by your schema will be encrypted automatically before it is stored, and decrypted when queried.
-
-```js
-(async () => {
- const db = await initEncryptedDatabase();
-
- // Insert encrypted data
- const doc = await db.secureData.insert({
- id: 'mySecretId',
- normalField: 'foobar',
- secretField: 'This is top secret data'
- });
-
- // Query encrypted data by its primary key or non-encrypted fields
- const fetchedDoc = await db.secureData.findOne({
- selector: {
- normalField: 'foobar'
- }
- }).exec(true);
- console.log(fetchedDoc.secretField); // 'This is top secret data'
-
- // Update data
- await fetchedDoc.patch({
- secretField: 'Updated secret data'
- });
-})();
-```
-
-**Note**: You can only query directly by non-encrypted fields or primary keys. Encrypted fields cannot be used in queries because they are stored as ciphertext in the database. A common approach is to have a small subset of fields that need to be queried unencrypted while storing any sensitive data in encrypted fields.
-
-## Best Practices for React Native Encryption
-
-- **Secure Password Handling**
- - Avoid hardcoding passwords or encryption keys.
- - Use secure storage solutions like React Native Keychain or react-native-encrypted-storage to fetch the database password at runtime:
-
-```js
-// Example: using react-native-keychain to securely retrieve a stored password
-import * as Keychain from 'react-native-keychain';
-
-async function getDatabasePassword() {
- const credentials = await Keychain.getGenericPassword();
- if (credentials) {
- return credentials.password;
- }
- throw new Error('No password stored in Keychain');
-}
-```
-
-- **Encrypt Attachments**:
-If you need to store files (images, text files, etc.), consider encrypting attachments. RxDB supports attachments that can be encrypted automatically, ensuring your files are protected:
-
-```ts
-import { createBlob } from 'rxdb/plugins/core';
-const doc = await await db.secureData.findOne({
- selector: {
- normalField: 'foobar'
- }
-}).exec(true);
-const attachment = await doc.putAttachment({
- id: 'encryptedFile.txt',
- data: createBlob('Sensitive content', 'text/plain'),
- type: 'text/plain',
-});
-```
-
-- **Optimize Performance**
- - If performance is critical, consider using the premium Web Crypto plugin, which leverages native APIs for faster encryption and decryption.
- - If big chunks of data are encrypted, store them in attachments instead of document fields. Attachments will only be decrypted on explicit fetches, not during queries.
-
-- **Use DevMode in Development**: RxDB's [DevMode Plugin](../dev-mode.md) can help validate your schema and encryption setup during development. Disable it in production for performance reasons.
-
-- **Secure Communication**:
- - Use HTTPS to secure network communication between the app and any backend services.
- - If you're synchronizing data to a server, ensure the data is also encrypted in transit. RxDB's [replication plugins](../replication.md) can work with secure endpoints to keep data consistent.
-
-- **SSL Pinning**: Consider SSL Pinning if you want to prevent man-in-the-middle attacks. SSL Pinning ensures the device trusts only the pinned certificate, preventing attackers from swapping out valid certificates with their own.
-
-## Follow Up
-
-- Learn how to use RxDB with the [RxDB Quickstart](../quickstart.md) for a guided introduction.
-- A good way to learn using RxDB database with React Native is to check out the [RxDB React Native example](https://github.com/pubkey/rxdb/tree/master/examples/react-native) and use that as a tutorial.
-- Check out the [RxDB GitHub repository](https://github.com/pubkey/rxdb) and leave a star ⭐ if you find it useful.
-- Learn more about the [RxDB encryption plugins](../encryption.md).
-
-By following these best practices and leveraging RxDB's powerful encryption plugins, you can build secure, performant, and robust React Native applications that keep your users' data safe.
diff --git a/docs/articles/reactjs-storage.html b/docs/articles/reactjs-storage.html
deleted file mode 100644
index b4b64b98ba6..00000000000
--- a/docs/articles/reactjs-storage.html
+++ /dev/null
@@ -1,254 +0,0 @@
-
-
-
-
-
-ReactJS Storage - From Basic LocalStorage to Advanced Offline Apps with RxDB | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ReactJS Storage – From Basic LocalStorage to Advanced Offline Apps with RxDB
-
Modern ReactJS applications often need to store data on the client side. Whether you’re preserving simple user preferences or building offline-ready features, choosing the right storage mechanism can make or break your development experience. In this guide, we’ll start with a basic localStorage approach for minimal data. Then, we’ll explore more powerful, reactive solutions via RxDB—including offline functionality, indexing, preact signals, and even encryption.
-
-
Part 1: Storing Data in ReactJS with LocalStorage
-
localStorage is a built-in browser API for storing key-value pairs in the user’s browser. It’s straightforward to set and get items—ideal for trivial preferences or small usage data.
Downsides of localStorage
-While localStorage is convenient for small amounts of data, it has certain limitations:
-
-
Synchronous: Reading or writing localStorage can block the main thread if data is large.
-
No advanced queries: You only store stringified objects by a single key. Searching or filtering requires manually scanning everything.
-
No concurrency or offline logic: If multiple tabs or users need to manipulate the same data, localStorage doesn’t handle concurrency or sync with a server.
-
No indexing: You can’t perform partial lookups or advanced matching.
-
-
For “remember user preference” use cases, localStorage is excellent. But if your app grows complex—needing structured data, large data sets, or offline-first features—you might quickly surpass localStorage’s utility.
While localStorage is simple, it’s limited to string-based key-value lookups and can be synchronous for all reads/writes. For more robust ReactJS storage needs, browsers also provide IndexedDB—a low-level, asynchronous API that can store larger amounts of JSON data with indexing.
-
LocalStorage:
-
-
Good for small amounts of data (like user settings or flags)
-
String-only storage
-
Single key-value access, no searching by subfields
-
-
IndexedDB:
-
-
Stores large JSON objects, able to index by multiple fields
-
Asynchronous and usually more scalable
-
More complicated to use directly (i.e., not as simple as .getItem())
-RxDB, as you’ll see, simplifies IndexedDB usage in ReactJS by adding a more intuitive layer for queries, reactivity, and advanced capabilities like encryption.
-
-
-
Part 3: Moving Beyond Basic Storage: RxDB for ReactJS
-
When data shapes get complex—large sets of nested documents, or you want offline sync to a server—RxDB can transform your approach to ReactJS storage. It stores documents in (usually) IndexedDB or alternative backends but offers a reactive, NoSQL-based interface.
Reactive Queries: In a React component, you can subscribe to a query via RxDB’s $ property, letting your UI automatically update when data changes. React components can subscribe to updates from .find() queries, letting the UI automatically reflect changes—perfect for dynamic offline-first apps.
By using these reactive queries, your React app knows exactly when data changes locally (or from another browser tab) or from remote sync, keeping your UI in sync effortlessly.
-
Part 4: Using Preact Signals Instead of Observables
-
RxDB typically exposes reactivity via RxJS observables. However, some developers prefer newer reactivity approaches like Preact Signals. RxDB supports them via a special plugin or advanced usage:
-
import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { PreactSignalsRxReactivityFactory } from 'rxdb/plugins/reactivity-preact-signals';
-
-(async function setUpRxDBWithSignals() {
- const db = await createRxDatabase({
- name: 'heroDB_signals',
- storage: getRxStorageLocalstorage(),
- reactivity: PreactSignalsRxReactivityFactory
- });
-
- // Create a signal-based query instead of using Observables:
- const collection = db.heroes;
- const heroesSignal = collection.find().$$; // signals version
- // Now you can reference heroesSignal() in Preact or React with adapter usage
-})();
-
Preact Signals rely on signals instead of Observables. Some developers find them more straightforward to adopt, especially for fine-grained reactivity. In ReactJS, you might still prefer RxJS-based subscriptions unless you add bridging code for signals.
For more advanced ReactJS storage needs—especially when sensitive user data is involved - you might want to encrypt stored documents at rest. RxDB provides a robust encryption plugin:
All data in the marked encrypted fields is automatically encrypted at rest. This is crucial if you store user credentials, private messages, or other personal data in your ReactJS application storage.
If you need multi-device or multi-user data synchronization, RxDB provides replication plugins for various endpoints (HTTP, GraphQL, CouchDB, Firestore, etc.). Your local offline changes can then merge automatically with a remote database whenever internet connectivity is restored.
If you’re looking to dive deeper into ReactJS storage topics and take full advantage of RxDB’s offline-first, real-time capabilities, here are some recommended resources:
RxDB Quickstart
-Get a step-by-step tutorial to create your first RxDB-based application in minutes.
-
-
-
RxDB GitHub Repository
-See the source code, open issues, and browse community-driven examples that integrate ReactJS, Preact Signals, and advanced features like encryption.
-
-
-
RxDB Encryption Plugins
-Learn how to encrypt fields in your collections, protecting user data and meeting compliance requirements.
MDN: Using the Web Storage API
-Refresh on localStorage basics, including best practices for small key-value data in traditional React apps.
-
-
-
With these follow-up steps, you can refine your reactjs storage strategy to meet your app’s unique needs, whether it’s simple user preferences or robust offline data syncing.
-
-
\ No newline at end of file
diff --git a/docs/articles/reactjs-storage.md b/docs/articles/reactjs-storage.md
deleted file mode 100644
index 855f8921e3f..00000000000
--- a/docs/articles/reactjs-storage.md
+++ /dev/null
@@ -1,266 +0,0 @@
-# ReactJS Storage - From Basic LocalStorage to Advanced Offline Apps with RxDB
-
-> Discover how to implement reactjs storage using localStorage for quick key-value data, then move on to more robust offline-first approaches with RxDB, IndexedDB, preact signals, encryption plugins, and more.
-
-# ReactJS Storage – From Basic LocalStorage to Advanced Offline Apps with RxDB
-
-Modern **ReactJS** applications often need to store data on the client side. Whether you’re preserving simple user preferences or building offline-ready features, choosing the right **storage** mechanism can make or break your development experience. In this guide, we’ll start with a basic **localStorage** approach for minimal data. Then, we’ll explore more powerful, reactive solutions via [RxDB](/)—including offline functionality, indexing, `preact signals`, and even encryption.
-
----
-
-## Part 1: Storing Data in ReactJS with LocalStorage
-
-`localStorage` is a built-in browser API for storing key-value pairs in the user’s browser. It’s straightforward to set and get items—ideal for trivial preferences or small usage data.
-
-```jsx
-import React, { useState, useEffect } from 'react';
-
-function LocalStorageExample() {
- const [username, setUsername] = useState(() => {
- const saved = localStorage.getItem('username');
- return saved ? JSON.parse(saved) : '';
- });
-
- useEffect(() => {
- localStorage.setItem('username', JSON.stringify(username));
- }, [username]);
-
- return (
-
- ReactJS LocalStorage Demo
- setUsername(e.target.value)}
- placeholder="Enter your username"
- />
- Stored: {username}
-
- );
-}
-
-export default LocalStorageExample;
-```
-
-**Pros** of localStorage in ReactJS:
-
-- Easy to implement quickly for minimal data
-- Built-in to the browser—no extra libs
-- Persistent across sessions
-
-**Downsides of localStorage**
-While localStorage is convenient for small amounts of data, it has certain limitations:
-
-- Synchronous: Reading or writing localStorage can block the main thread if data is large.
-- No advanced queries: You only store stringified objects by a single key. Searching or filtering requires manually scanning everything.
-- No concurrency or offline logic: If multiple tabs or users need to manipulate the same data, localStorage doesn’t handle concurrency or sync with a server.
-- No indexing: You can’t perform partial lookups or advanced matching.
-
-For “remember user preference” use cases, localStorage is excellent. But if your app grows complex—needing structured data, large data sets, or offline-first features—you might quickly surpass localStorage’s utility.
-
-## Part 2: LocalStorage vs. IndexedDB
-
-While localStorage is simple, it’s limited to string-based key-value lookups and can be synchronous for all reads/writes. For more robust ReactJS storage needs, browsers also provide IndexedDB—a low-level, asynchronous API that can store larger amounts of JSON data with indexing.
-
-**LocalStorage:**
-
-- Good for small amounts of data (like user settings or flags)
-- String-only storage
-- Single key-value access, no searching by subfields
-
-**IndexedDB:**
-
-- Stores [large](./indexeddb-max-storage-limit.md) JSON objects, able to index by multiple fields
-- Asynchronous and usually more scalable
-- More complicated to use directly (i.e., not as simple as .getItem())
-[RxDB](/), as you’ll see, simplifies [IndexedDB](../rx-storage-indexeddb.md) usage in ReactJS by adding a more intuitive layer for queries, reactivity, and advanced capabilities like [encryption](../encryption.md).
-
-
-
-
-
-
-
-## Part 3: Moving Beyond Basic Storage: RxDB for ReactJS
-
-When data shapes get complex—large sets of nested documents, or you want offline sync to a server—RxDB can transform your approach to ReactJS storage. It stores documents in (usually) IndexedDB or alternative backends but offers a reactive, NoSQL-based interface.
-
-### RxDB Quick Example (Observables)
-
-```ts
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-(async function setUpRxDB() {
- const db = await createRxDatabase({
- name: 'heroDB',
- storage: getRxStorageLocalstorage(),
- multiInstance: false
- });
-
- const heroSchema = {
- title: 'hero schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- name: { type: 'string' },
- power: { type: 'string' }
- },
- required: ['id', 'name']
- };
-
- await db.addCollections({ heroes: { schema: heroSchema } });
-
- // Insert a doc
- await db.heroes.insert({ id: '1', name: 'AlphaHero', power: 'Lightning' });
-
- // Query docs once
- const allHeroes = await db.heroes.find().exec();
- console.log('Heroes: ', allHeroes);
-})();
-```
-
-Reactive Queries: In a React component, you can subscribe to a query via RxDB’s $ property, letting your UI automatically update when data changes. React components can subscribe to updates from .find() queries, letting the UI automatically reflect changes—perfect for dynamic offline-first apps.
-
-```tsx
-import React, { useEffect, useState } from 'react';
-
-function HeroList({ collection }) {
- const [heroes, setHeroes] = useState([]);
-
- useEffect(() => {
- const query = collection.find();
- // query.$ is an RxJS Observable that emits whenever data changes
- const sub = query.$.subscribe(newHeroes => {
- setHeroes(newHeroes);
- });
-
- return () => sub.unsubscribe(); // clean up subscription
- }, [collection]);
-
- return (
-
- {heroes.map(hero => (
-
- {hero.name} - Power: {hero.power}
-
- ))}
-
- );
-}
-
-export default HeroList;
-```
-
-
-
-By using these reactive queries, your React app knows exactly when data changes locally (or from another browser tab) or from remote sync, keeping your UI in sync effortlessly.
-
-## Part 4: Using Preact Signals Instead of Observables
-
-RxDB typically exposes reactivity via RxJS observables. However, some developers prefer newer reactivity approaches like Preact Signals. RxDB supports them via a special plugin or advanced usage:
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-import { PreactSignalsRxReactivityFactory } from 'rxdb/plugins/reactivity-preact-signals';
-
-(async function setUpRxDBWithSignals() {
- const db = await createRxDatabase({
- name: 'heroDB_signals',
- storage: getRxStorageLocalstorage(),
- reactivity: PreactSignalsRxReactivityFactory
- });
-
- // Create a signal-based query instead of using Observables:
- const collection = db.heroes;
- const heroesSignal = collection.find().$$; // signals version
- // Now you can reference heroesSignal() in Preact or React with adapter usage
-})();
-```
-
-Preact Signals rely on `signals` instead of `Observables`. Some developers find them more straightforward to adopt, especially for fine-grained reactivity. In ReactJS, you might still prefer RxJS-based subscriptions unless you add bridging code for signals.
-
-## Part 5: Encrypting the Storage with RxDB
-
-For more advanced ReactJS storage needs—especially when sensitive user data is involved - you might want to encrypt stored documents at rest. RxDB provides a robust [encryption plugin](../encryption.md):
-
-```ts
-import { createRxDatabase } from 'rxdb';
-import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-(async function secureSetup() {
- const encryptedStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageLocalstorage()
- });
-
- // Provide a password for encryption
- const db = await createRxDatabase({
- name: 'secureReactStorage',
- storage: encryptedStorage,
- password: 'MyStrongPassword123'
- });
-
- await db.addCollections({
- secrets: {
- schema: {
- title: 'secret schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string' },
- secretInfo: { type: 'string' }
- },
- required: ['id'],
- encrypted: ['secretInfo'] // field to encrypt
- }
- }
- });
-})();
-```
-
-All data in the marked `encrypted` fields is automatically encrypted at rest. This is crucial if you store user credentials, private messages, or other personal data in your ReactJS application storage.
-
-## Offline Sync
-If you need multi-device or multi-user data synchronization, RxDB provides [replication plugins](../replication.md) for various endpoints (HTTP, [GraphQL](../replication-graphql.md), [CouchDB](../replication-couchdb.md), [Firestore](../replication-firestore.md), etc.). Your [local offline](../offline-first.md) changes can then merge automatically with a remote database whenever internet connectivity is restored.
-
-## Overview: [localStorage vs IndexedDB vs RxDB](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md)
-
-| **Characteristic** | **localStorage** | **IndexedDB** | **RxDB** |
-|--------------------------|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
-| **Data Model** | Key-value store (only strings) | Low-level, JSON-like storage engine with object stores and indexes | NoSQL JSON documents with optional JSON-Schema |
-| **Query Capabilities** | Basic get/set by key; manual parse for more complex searches | Index-based queries, but API is fairly verbose; lacks a high-level query language | JSON-based queries, optional indexes, real-time reactivity |
-| **Observability** | None. Must re-fetch data yourself. | None natively. Must implement eventing or manual re-check. | Built-in reactivity. UI auto-updates via Observables or Preact signals |
-| **Large Data Usage** | Not recommended for large data (blocking, synchronous calls) | Better for large amounts of data, asynchronous reads/writes | Scales for medium to large data. Uses IndexedDB or other storages under the hood |
-| **Concurrency** | Minimal. Overwrites if multiple tabs write simultaneously | Multiple tabs can open the same DB, but must handle concurrency logic carefully | Multi-instance concurrency with built-in conflict resolution plugins if needed |
-| **Offline Sync** | None. Purely local. | None out of the box. Must be implemented manually | Built-in replication to remote endpoints (HTTP, GraphQL, CouchDB, etc.) for offline-first usage |
-| **Encryption** | Not supported natively | Not supported natively; must encrypt data manually before storing | Encryption plugins available. Supports field-level encryption at rest |
-| **Usage** | Great for small flags or settings
-
-## Follow Up
-
-If you’re looking to dive deeper into **ReactJS storage** topics and take full advantage of RxDB’s offline-first, real-time capabilities, here are some recommended resources:
-
-- **[RxDB Official Documentation](../overview.md)**
- Explore detailed guides on setting up storage adapters, defining [JSON schemas](../rx-schema.md), [handling conflicts](../transactions-conflicts-revisions.md), and enabling [offline synchronization](../replication.md).
-
-- **[RxDB Quickstart](https://rxdb.info/quickstart.html)**
- Get a step-by-step tutorial to create your first RxDB-based application in minutes.
-
-- **[RxDB GitHub Repository](https://github.com/pubkey/rxdb)**
- See the source code, open issues, and browse community-driven examples that integrate ReactJS, Preact Signals, and advanced features like encryption.
-
-- **[RxDB Encryption Plugins](https://rxdb.info/encryption.html)**
- Learn how to encrypt fields in your collections, protecting user data and meeting compliance requirements.
-
-- **[Preact Signals React Integration (Example)](https://github.com/preactjs/signals#react)**
- If you want to combine React with signals-based reactivity, check out example code and bridging approaches.
-
-- **[MDN: Using the Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)**
- Refresh on localStorage basics, including best practices for small key-value data in traditional React apps.
-
-With these follow-up steps, you can refine your **reactjs storage** strategy to meet your app’s unique needs, whether it’s simple user preferences or robust offline data syncing.
diff --git a/docs/articles/realtime-database.html b/docs/articles/realtime-database.html
deleted file mode 100644
index dfe2e57dbb8..00000000000
--- a/docs/articles/realtime-database.html
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-What Really Is a Realtime Database? | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
I have been building RxDB, a NoSQL realtime JavaScript database for many years.
-Often people get confused by the word realtime database, because the word realtime is so vaguely defined that it can mean everything and nothing at the same time.
-
In this article we will explore what a realtime database is, and more important, what it is not.
When "normal" developers hear the word "realtime", they think of Real-time computing (RTC). Real-time computing is a type of computer processing that guarantees specific response times for tasks or events, crucial in applications like industrial control, automotive systems, and aerospace. It relies on specialized operating systems (RTOS) to ensure predictability and low latency. Hard real-time systems must never miss deadlines, while soft real-time systems can tolerate occasional delays. Real-time responses are often understood to be in the order of milliseconds, and sometimes microseconds.
-
Consider the role of real-time computing in car airbags: sensors detect collision force, swiftly process the data, and immediately decide to deploy the airbags within milliseconds. Such rapid action is imperative for safeguarding passengers. Hence, the controlling chip must guarantee a certain response time - it must operate in "realtime".
-
But when people talk about realtime databases, especially in the web-development world, they almost never mean realtime, as in realtime computing, they mean something else.
-In fact, with any programming language that run on end users devices, it is not even possible to built a "real" realtime database. A program, like a JavaScript (browser or Node.js) process, can be halted by the operating systems task manager at any time and therefore it will never be able to guarantee specific response times. To build a realtime computing database, you would need a realtime capable operating system.
When talking about realtime databases, most people refer to realtime, as in realtime replication.
-Often they mean a very specific product which is the Firebase Realtime Database (not the Firestore).
-
-
In the context of the Firebase Realtime Database, "realtime" means that data changes are synchronized and delivered to all connected clients or devices as soon as they occur, typically within milliseconds. This means that when any client updates, adds, or removes data in the database, all other clients that are connected to the same database instance receive those updates instantly, without the need for manual polling or frequent HTTP requests.
-
In short, when replicating data between databases, instead of polling, we use a websocket connection to live-stream all changes between the server and the clients, this is labeled as "realtime database". A similar thing can be done with RxDB and the RxDB Replication Plugins.
In the context of realtime client-side applications, "realtime" refers to the immediate or near-instantaneous processing and response to events or data inputs. When data changes, the application must directly update to reflect the new data state, without any user interaction or delay. Notice that the change to the data could have come from any source, like a user action, an operation in another browser tab, or even an operation from another device that has been replicated to the client.
-
-
In contrast to push-pull based databases (e.g., MySQL or MongoDB servers), a realtime database contains features which make it easy to build realtime applications. For example with RxDB you can not only fetch query results once, but instead you can subscribe to a query and directly update the HTML dom tree whenever the query has a new result set:
-
await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
-})
-.$ // The $ returns an observable that emits whenever the query's result set changes.
-.subscribe(aliveHeroes => {
- // Refresh the HTML list each time there are new query results.
- const newContent = aliveHeroes.map(doc => '<li>' + doc.name + '</li>');
- document.getElementById('#myList').innerHTML = newContent;
-});
-
-// You can even subscribe to any RxDB document's fields.
-myDocument.firstName$.subscribe(newName => console.log('name is: ' + newName));
-
A competent realtime application is engineered to offer feedback or results swiftly, ideally within milliseconds to microseconds. Ideally, a data modification should be processed in under 16 milliseconds (since 1 second divided by 60 frames equals 16.66ms) to ensure users don't perceive any lag from input to visualization. RxDB utilizes the EventReduce algorithm to manage changes more swiftly than 16ms. However, it can never assure fixed response times as a "realtime computing database" would.
-
-
\ No newline at end of file
diff --git a/docs/articles/realtime-database.md b/docs/articles/realtime-database.md
deleted file mode 100644
index fc0539aa8aa..00000000000
--- a/docs/articles/realtime-database.md
+++ /dev/null
@@ -1,75 +0,0 @@
-# What Really Is a Realtime Database?
-
-> Discover how RxDB merges realtime replication and dynamic updates to deliver seamless data sync across browsers, devices, and servers - instantly.
-
-# What is a realtime database?
-
-I have been building [RxDB](https://rxdb.info/), a NoSQL **realtime** JavaScript database for many years.
-Often people get confused by the word **realtime database**, because the word **realtime** is so vaguely defined that it can mean everything and nothing at the same time.
-
-In this article we will explore what a realtime database is, and more important, what it is not.
-
-
-
-
-
-
-
-## Realtime as in **realtime computing**
-
-When "normal" developers hear the word "realtime", they think of **Real-time computing (RTC)**. Real-time computing is a type of computer processing that **guarantees specific response times** for tasks or events, crucial in applications like industrial control, automotive systems, and aerospace. It relies on specialized operating systems (RTOS) to ensure predictability and low latency. Hard real-time systems must never miss deadlines, while soft real-time systems can tolerate occasional delays. Real-time responses are often understood to be in the order of milliseconds, and sometimes microseconds.
-
-Consider the role of real-time computing in car airbags: sensors detect collision force, swiftly process the data, and immediately decide to deploy the airbags within milliseconds. Such rapid action is imperative for safeguarding passengers. Hence, the controlling chip must **guarantee a certain response time** - it must operate in "realtime".
-
-But when people talk about **realtime databases**, especially in the web-development world, they almost never mean realtime, as in **realtime computing**, they mean something else.
-In fact, with any programming language that run on end users devices, it is not even possible to built a "real" realtime database. A program, like a JavaScript ([browser](./browser-database.md) or [Node.js](../nodejs-database.md)) process, can be halted by the operating systems task manager at any time and therefore it will never be able to guarantee specific response times. To build a realtime computing database, you would need a realtime capable operating system.
-
-## Real time Database as in **realtime replication**
-
-When talking about realtime databases, most people refer to realtime, as in realtime replication.
-Often they mean a very specific product which is the **Firebase Realtime Database** (not the [Firestore](../replication-firestore.md)).
-
-
-
-In the context of the Firebase Realtime Database, "realtime" means that data changes are synchronized and delivered to all connected clients or devices as soon as they occur, typically within milliseconds. This means that when any client updates, adds, or removes data in the database, all other clients that are connected to the same database instance receive those updates instantly, without the need for manual polling or frequent HTTP requests.
-
-In short, when replicating data between databases, instead of polling, we use a [websocket connection](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) to live-stream all changes between the server and the clients, this is labeled as "realtime database". A similar thing can be done with RxDB and the [RxDB Replication Plugins](../replication.md).
-
-
-
-
-
-## Realtime as in **realtime applications**
-
-In the context of realtime client-side applications, "realtime" refers to the immediate or near-instantaneous processing and response to events or data inputs. When data changes, the application must directly update to reflect the new data state, without any user interaction or delay. Notice that the change to the data could have come from any source, like a user action, an operation in another browser tab, or even an operation from another device that has been replicated to the client.
-
-
-
-In contrast to push-pull based databases (e.g., MySQL or MongoDB servers), a realtime database contains **features which make it easy to build realtime applications**. For example with RxDB you can not only fetch query results once, but instead you can subscribe to a query and directly update the HTML dom tree whenever the query has a new result set:
-
-```ts
-await db.heroes.find({
- selector: {
- healthpoints: {
- $gt: 0
- }
- }
-})
-.$ // The $ returns an observable that emits whenever the query's result set changes.
-.subscribe(aliveHeroes => {
- // Refresh the HTML list each time there are new query results.
- const newContent = aliveHeroes.map(doc => '' + doc.name + '');
- document.getElementById('#myList').innerHTML = newContent;
-});
-
-// You can even subscribe to any RxDB document's fields.
-myDocument.firstName$.subscribe(newName => console.log('name is: ' + newName));
-```
-
-A competent realtime application is engineered to offer feedback or results swiftly, ideally within milliseconds to microseconds. Ideally, a data modification should be processed in under **16 milliseconds** (since 1 second divided by 60 frames equals 16.66ms) to ensure users don't perceive any lag from input to visualization. RxDB utilizes the [EventReduce algorithm](https://github.com/pubkey/event-reduce) to manage changes more swiftly than 16ms. However, it can never assure fixed response times as a "realtime computing database" would.
-
-## Follow Up
-
-- Dive into the [RxDB Quickstart](https://rxdb.info/quickstart.html)
-- Discover more about the [RxDB realtime Sync Engine](../replication.md)
-- Join the conversation at [RxDB Chat](https://rxdb.info/chat/)
diff --git a/docs/articles/vue-database.html b/docs/articles/vue-database.html
deleted file mode 100644
index 76449b37750..00000000000
--- a/docs/articles/vue-database.html
+++ /dev/null
@@ -1,188 +0,0 @@
-
-
-
-
-
-RxDB as a Database in a Vue.js Application | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In the modern web ecosystem, Vue has become a leading choice for building highly performant, reactive single-page applications (SPAs). However, while Vue excels at managing and updating the user interface, robust and efficient data handling also plays a pivotal role in delivering a great user experience. Enter RxDB, a reactive JavaScript database that runs in the browser (and beyond), offering significant capabilities such as offline-first data handling, real-time synchronization, and straightforward integration with Vue's reactivity system.
-
This article explores how RxDB works, why it's a perfect match for Vue, and how you can leverage it to build more engaging, performant, and data-resilient Vue applications.
Vue is renowned for its lightweight core and flexible architecture centered around reactive state management and reusable components. However, modern Vue applications often require:
-
-
Offline Capabilities: Allowing users to continue working even without internet access.
-
Real-Time Updates: Keeping UI data in sync with changes as they occur, whether locally or from other connected clients.
-
Improved Performance: Reducing server round trips and leveraging local storage for faster data operations.
-
Scalable Data Handling: Managing increasingly large datasets or complex queries right in the browser.
-
-
While you can store data in Vuex/Pinia stores or via direct AJAX calls, these solutions may not suffice when your application demands a full-featured offline-first database or complex synchronization with a server. RxDB addresses these needs with a dedicated, reactive, browser-based database that pairs seamlessly with Vue's reactivity system.
RxDB - short for Reactive Database - is built on the principle of combining NoSQL database capabilities with reactive programming. It runs inside your client-side environment (browser, Node.js, or mobile devices) and provides:
-
-
Real-Time Reactivity: Automatically updates subscribed components whenever data changes.
-
Offline-First Approach: Stores data locally and syncs with the server when online connectivity is restored.
-
Data Replication: Effortlessly keeps data synchronized across multiple tabs, devices, or server instances.
-
Multi-Tab Support: Seamlessly propagates changes to all open tabs in the user's browser.
-
Observable Queries: Automatically refresh the result set when documents in your queried collection change.
Compared to traditional approaches - like raw IndexedDB or local storage - RxDB adds a powerful, reactive layer that simplifies your data flow. While tools like Vuex or Pinia are great for state management, they are not fully fledged databases with features like replication, conflict resolution, and offline persistence. RxDB bridges the gap by providing an integrated data handling solution tailor-made for modern, data-intensive Vue applications.
Within your Vue project, you can set up an RxDB instance in a dedicated file or a Vue plugin. Below is an example using Localstorage as the storage engine:
RxDB queries return RxJS observables (.$). Vue can automatically update components when data changes if you manually subscribe and store results in Vue refs/reactive objects, or if you use RxDB's custom reactivity for Vue.
OPFS RxStorage: Uses the File System Access API for even faster storage in modern browsers.
-
Memory RxStorage: Stores data in memory, ideal for tests or ephemeral data.
-
SQLite RxStorage: Runs SQLite, which can be compiled to WebAssembly for the browser. While possible, it typically carries a larger bundle size compared to native browser APIs like IndexedDB or OPFS.
-
-
Choose the storage option that best aligns with your Vue application's requirements for performance, persistence, and platform compatibility.
-
Synchronizing Data with RxDB between Clients and Servers
-
RxDB champions an offline-first approach: data is kept locally so that your Vue app remains usable, even without internet. When connectivity is restored, RxDB ensures your local changes synchronize to the server, resolving conflicts as necessary.
-
-
-
Real-Time Synchronization: With RxDB's replication plugins, any local change can be instantly pushed to a remote endpoint while pulling down remote changes to ensure consistency.
-
Conflict Resolution: In multi-user scenarios, conflicts may arise if two clients update the same document simultaneously. RxDB provides hooks to handle and resolve these gracefully.
-
Scalable Architecture: By reducing reliance on continuous server requests, you can lighten server load and deliver a more responsive user experience.
Vue applications can seamlessly function offline by leveraging RxDB's local database storage. The moment the network is restored, all unsynced data is pushed to the server. This capability is particularly beneficial for Progressive Web Apps (PWAs) and scenarios with spotty connectivity.
Beyond simply returning data, RxDB queries emit observables that respond to any change in the underlying documents. This real-time approach can drastically simplify state management, since updates flow directly into your Vue components without additional manual wiring.
For applications handling sensitive information, RxDB supports encryption of local data. Your data is stored securely in the browser, protecting it from unauthorized access.
By defining indexes on frequently searched fields, you can speed up queries and reduce overall resource usage. This is crucial for larger datasets where performance might otherwise degrade.
This optimization shortens field names in stored JSON documents, thereby reducing storage space and potentially improving performance for read/write operations.
If your users open multiple tabs of your Vue application, RxDB ensures data is synchronized across all instances in real time. Changes made in one tab are immediately reflected in others, creating a unified user experience.
Here are some recommendations to get the most out of RxDB in your Vue projects:
-
-
Centralize Database Creation: Initialize and configure RxDB in a dedicated file or plugin, ensuring only one database instance is created.
-
Leverage Vue's Composition API or a Global Store: Use watchers, refs, or a store like Pinia to neatly manage data subscriptions and updates, preventing scattered subscription logic.
-
Async Subscriptions: Prefer using Vue's lifecycle hooks and the Composition API to manage subscriptions. Clean up subscriptions when components unmount or no longer need the data.
-
Optimize Queries and Indexes: Only query the data you need, and define indexes to speed up lookups.
-
Test Offline Scenarios: Make sure your offline logic works as expected by simulating network disconnections and reconnections.
-
Plan Conflict Resolution: For multi-user apps, decide how to merge concurrent changes to prevent data inconsistencies.
To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-
RxDB GitHub Repository: Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-
RxDB Quickstart: Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-
RxDB Reactivity for Vue: Discover how RxDB observables can directly produce Vue refs, simplifying integration with your Vue components.
-
RxDB Vue Example at GitHub: Explore an official Vue example to see RxDB in action within a Vue application.
-
RxDB Examples: Browse even more official examples to learn best practices you can apply to your own projects.
-
-
\ No newline at end of file
diff --git a/docs/articles/vue-database.md b/docs/articles/vue-database.md
deleted file mode 100644
index 9616fedefd6..00000000000
--- a/docs/articles/vue-database.md
+++ /dev/null
@@ -1,192 +0,0 @@
-# RxDB as a Database in a Vue.js Application
-
-> Level up your Vue projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.
-
-# RxDB as a Database in a Vue Application
-
-In the modern web ecosystem, [Vue](https://vuejs.org/) has become a leading choice for building highly performant, reactive single-page applications (SPAs). However, while Vue excels at managing and updating the user interface, robust and efficient data handling also plays a pivotal role in delivering a great user experience. Enter [RxDB](https://rxdb.info/), a reactive JavaScript database that runs in the browser (and beyond), offering significant capabilities such as offline-first data handling, real-time synchronization, and straightforward integration with Vue's reactivity system.
-
-This article explores how RxDB works, why it's a perfect match for Vue, and how you can leverage it to build more engaging, performant, and data-resilient Vue applications.
-
-
-
-
-
-
-
-## Why Vue Applications Need a Database
-Vue is renowned for its lightweight core and flexible architecture centered around reactive state management and reusable components. However, modern Vue applications often require:
-
-- **Offline Capabilities:** Allowing users to continue working even without internet access.
-- **Real-Time Updates:** Keeping UI data in sync with changes as they occur, whether locally or from other connected clients.
-- **Improved Performance:** Reducing server round trips and leveraging local storage for faster data operations.
-- **Scalable Data Handling:** Managing increasingly large datasets or complex queries right in the browser.
-
-While you can store data in Vuex/Pinia stores or via direct AJAX calls, these solutions may not suffice when your application demands a full-featured offline-first database or complex synchronization with a server. RxDB addresses these needs with a dedicated, reactive, browser-based database that pairs seamlessly with Vue's reactivity system.
-
-## Introducing RxDB as a Database Solution
-RxDB - short for Reactive Database - is built on the principle of combining [NoSQL database](./in-memory-nosql-database.md) capabilities with reactive programming. It runs inside your client-side environment (browser, [Node.js](../nodejs-database.md), or [mobile devices](./mobile-database.md)) and provides:
-
-1. **Real-Time Reactivity**: Automatically updates subscribed components whenever data changes.
-2. **Offline-First Approach**: Stores data locally and syncs with the server when online connectivity is restored.
-3. **Data Replication**: Effortlessly keeps data synchronized across multiple tabs, devices, or server instances.
-4. **Multi-Tab Support**: Seamlessly propagates changes to all open tabs in the user's [browser](./browser-database.md).
-5. **Observable Queries**: Automatically refresh the result set when documents in your queried collection change.
-
-### RxDB vs. Other Vue Database Options
-Compared to traditional approaches - like raw IndexedDB or local storage - RxDB adds a powerful, reactive layer that simplifies your data flow. While tools like Vuex or Pinia are great for state management, they are not fully fledged databases with features like replication, conflict resolution, and offline persistence. RxDB bridges the gap by providing an integrated data handling solution tailor-made for modern, data-intensive Vue applications.
-
-## Getting Started with RxDB
-Let's break down the essentials for using RxDB within a Vue application.
-
-### Installation
-You can install RxDB (and RxJS, which it depends on) via npm or yarn:
-
-```bash
-npm install rxdb rxjs
-```
-
-## Creating and Configuring Your Database
-
-Within your Vue project, you can set up an RxDB instance in a dedicated file or a Vue plugin. Below is an example using [Localstorage](./localstorage.md) as the storage engine:
-
-```ts
-// db.js
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-export async function initDatabase() {
- const db = await createRxDatabase({
- name: 'heroesdb',
- storage: getRxStorageLocalstorage(),
- password: 'myPassword', // optional encryption password
- multiInstance: true, // multi-tab support
- eventReduce: true // optimize event handling
- });
-
- await db.addCollections({
- hero: {
- schema: {
- title: 'hero schema',
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: { type: 'string' },
- name: { type: 'string' },
- healthpoints: { type: 'number' }
- }
- }
- }
- });
-
- return db;
-}
-```
-
-After creating the RxDB instance, you can share it across your application (for example, by providing it in a plugin or a global property in Vue).
-
-## Vue Reactivity and RxDB Observables
-
-RxDB queries return RxJS observables (`.$`). Vue can automatically update components when data changes if you manually subscribe and store results in Vue refs/reactive objects, or if you use RxDB's [custom reactivity for Vue](../reactivity.md).
-
-**Example with Vue 3 Composition API:**
-
-```js
-// HeroList.vue
-
-
-
-
-
- {{ hero.name }} - HP: {{ hero.healthpoints }}
-
-
-
-```
-
-## Different RxStorage Layers for RxDB
-
-RxDB supports multiple storage backends - called "RxStorage layers" - giving you flexibility in how data is persisted:
-
-- [LocalStorage RxStorage](../rx-storage-localstorage.md): Uses the browsers localstorage API.
-- [IndexedDB RxStorage](../rx-storage-indexeddb.md): Direct usage of native IndexedDB.
-- [OPFS RxStorage](../rx-storage-opfs.md): Uses the File System Access API for even faster storage in modern browsers.
-- [Memory RxStorage](../rx-storage-memory.md): Stores data in memory, ideal for tests or ephemeral data.
-- [SQLite RxStorage](../rx-storage-sqlite.md): Runs SQLite, which can be compiled to WebAssembly for the browser. While possible, it typically carries a larger bundle size compared to native browser APIs like [IndexedDB or OPFS](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md).
-
-Choose the storage option that best aligns with your Vue application's requirements for performance, persistence, and platform compatibility.
-
-## Synchronizing Data with RxDB between Clients and Servers
-
-RxDB champions an offline-first approach: data is kept locally so that your Vue app remains usable, even without internet. When connectivity is restored, RxDB ensures your local changes synchronize to the server, resolving conflicts as necessary.
-
-- [Real-Time Synchronization](./realtime-database.md): With RxDB's replication plugins, any local change can be instantly pushed to a remote endpoint while pulling down remote changes to ensure consistency.
-- **Conflict Resolution**: In multi-user scenarios, conflicts may arise if two clients update the same document simultaneously. RxDB provides hooks to handle and resolve these gracefully.
-- **Scalable Architecture**: By reducing reliance on continuous server requests, you can lighten server load and deliver a more responsive user experience.
-
-## Advanced RxDB Features and Techniques
-
-### Offline-First Approach
-Vue applications can seamlessly function offline by leveraging RxDB's local database storage. The moment the network is restored, all unsynced data is pushed to the server. This capability is particularly beneficial for Progressive Web Apps (PWAs) and scenarios with spotty connectivity.
-
-### Observable Queries and Change Streams
-Beyond simply returning data, RxDB queries emit observables that respond to any change in the underlying documents. This real-time approach can drastically simplify state management, since updates flow directly into your Vue components without additional manual wiring.
-
-### Encryption of Local Data
-For applications handling sensitive information, RxDB supports [encryption](../encryption.md) of local data. Your data is stored securely in the browser, protecting it from unauthorized access.
-
-### Indexing and Performance Optimization
-By [defining indexes](../rx-schema.md) on frequently searched fields, you can speed up queries and reduce overall resource usage. This is crucial for larger datasets where performance might otherwise degrade.
-
-### JSON Key Compression
-This [optimization](../key-compression.md) shortens field names in stored JSON documents, thereby reducing storage space and potentially improving performance for read/write operations.
-
-### Multi-Tab Support
-If your users open multiple tabs of your Vue application, RxDB ensures data is synchronized across all instances in real time. Changes made in one tab are immediately reflected in others, creating a unified user experience.
-
-
-
-## Best Practices for Using RxDB in Vue
-
-Here are some recommendations to get the most out of RxDB in your Vue projects:
-
-- Centralize Database Creation: Initialize and configure RxDB in a dedicated file or plugin, ensuring only one database instance is created.
-- Leverage Vue's Composition API or a Global Store: Use watchers, refs, or a store like Pinia to neatly manage data subscriptions and updates, preventing scattered subscription logic.
-- Async Subscriptions: Prefer using Vue's lifecycle hooks and the Composition API to manage subscriptions. Clean up subscriptions when components unmount or no longer need the data.
-- Optimize Queries and Indexes: Only query the data you need, and define indexes to speed up lookups.
-- Test [Offline Scenarios](../offline-first.md): Make sure your offline logic works as expected by simulating network disconnections and reconnections.
-- [Plan Conflict Resolution](../transactions-conflicts-revisions.md): For multi-user apps, decide how to merge concurrent changes to prevent data inconsistencies.
-
-## Follow Up
-
-To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:
-
-- [RxDB GitHub Repository](/code/): Visit the official GitHub repository of RxDB to access the source code, documentation, and community support.
-- [RxDB Quickstart](../quickstart.md): Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects.
-- [RxDB Reactivity for Vue](../reactivity.md): Discover how RxDB observables can directly produce Vue refs, simplifying integration with your Vue components.
-- [RxDB Vue Example at GitHub](https://github.com/pubkey/rxdb/tree/master/examples/vue): Explore an official Vue example to see RxDB in action within a Vue application.
-- [RxDB Examples](https://github.com/pubkey/rxdb/tree/master/examples): Browse even more official examples to learn best practices you can apply to your own projects.
diff --git a/docs/articles/vue-indexeddb.html b/docs/articles/vue-indexeddb.html
deleted file mode 100644
index 496f0791234..00000000000
--- a/docs/articles/vue-indexeddb.html
+++ /dev/null
@@ -1,228 +0,0 @@
-
-
-
-
-
-IndexedDB Database in Vue Apps - The Power of RxDB | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
IndexedDB Database in Vue Apps - The Power of RxDB
-
Building robust, offline-capable Vue applications often involves leveraging browser storage solutions to manage data. IndexedDB is one such powerful tool, but its raw API can be challenging to work with directly. RxDB abstracts away much of IndexedDB's complexity, providing a more developer-friendly experience. In this article, we'll explore what IndexedDB is, why it's beneficial in Vue applications, the challenges of using plain IndexedDB, and how RxDB can simplify your development process while adding advanced features.
IndexedDB is a low-level API for storing significant amounts of structured data in the browser. It provides a transactional database system that can store key-value pairs, complex objects, and more. This storage engine is asynchronous and supports advanced data types, making it suitable for offline storage and complex web applications.
When building Vue applications, IndexedDB can play a crucial role in enhancing both performance and user experience. Here are some reasons to consider using IndexedDB:
-
-
Offline-First / Local-First: By storing data locally, your application remains functional even without an internet connection.
-
Performance: Using local data means zero latency and no loading spinners, as data doesn't need to be fetched over a network.
-
Easier Implementation: Replicating all data to the client once is often simpler than implementing multiple endpoints for each user interaction.
-
Scalability: Local data reduces server load because queries run on the client side, decreasing server bandwidth and processing requirements.
While IndexedDB itself is powerful, its native API comes with several drawbacks for everyday application developers:
-
-
Callback-Based API: IndexedDB was originally designed around callbacks rather than modern Promises, making asynchronous code more cumbersome.
-
Complexity: IndexedDB is low-level, intended for library developers rather than for app developers who just want to store and query data easily.
-
Basic Query API: Its rudimentary query capabilities limit how you can efficiently perform complex queries. Libraries like RxDB offer more advanced querying and indexing.
-
TypeScript Support: Ensuring good TypeScript support with IndexedDB is challenging, especially when trying to maintain schema consistency.
-
Lack of Observable API: IndexedDB doesn't provide an observable API out of the box, making it hard to automatically update your Vue app in real time. RxDB solves this by enabling you to observe queries or specific documents.
-
Cross-Tab Communication: Managing cross-tab updates in plain IndexedDB is difficult. RxDB handles this seamlessly - changes in one tab automatically affect observed data in others.
-
Missing Advanced Features: Features like encryption or compression aren't built into IndexedDB, but they are available via RxDB.
-
Limited Platform Support: IndexedDB is browser-only. RxDB offers swappable storages so you can reuse the same data layer code in mobile or desktop environments.
RxDB excels in providing reactive data capabilities, ideal for real-time applications. Subscribing to queries automatically updates your Vue components when underlying data changes - even across browser tabs.
-
-
Using RxJS Observables with Vue 3 Composition API
-
Here's an example of a Vue component that subscribes to live data updates:
If you're exploring Vue's reactivity transforms or signals, RxDB also offers custom reactivity factories (premium plugins are required). This allows queries to emit data as signals instead of traditional Observables.
-
const heroesSignal = db.heroes.find().$$; // $$ indicates a reactive result
-
-
With this, in your Vue template or script, you can directly read from heroesSignal()
-
<template>
- <div>
- <h2>Hero List</h2>
- <ul>
- <!-- we read heroesSignal.value which is always up to date -->
- <li v-for="hero in heroesSignal.value" :key="hero.id">
- <strong>{{ hero.name }}</strong> - {{ hero.power }}
- </li>
- </ul>
- </div>
-</template>
A comprehensive example of using RxDB within a Vue application can be found in the RxDB GitHub repository. This repository contains sample applications, showcasing best practices and demonstrating how to integrate RxDB for various use cases.
By leveraging RxDB on top of IndexedDB, you can create highly responsive, offline-capable Vue applications without dealing with the low-level complexities of IndexedDB directly. With reactive queries, seamless cross-tab communication, and powerful advanced features, RxDB becomes an invaluable tool in modern web development.
-
-
\ No newline at end of file
diff --git a/docs/articles/vue-indexeddb.md b/docs/articles/vue-indexeddb.md
deleted file mode 100644
index 7f869a47362..00000000000
--- a/docs/articles/vue-indexeddb.md
+++ /dev/null
@@ -1,242 +0,0 @@
-# IndexedDB Database in Vue Apps - The Power of RxDB
-
-> Learn how RxDB simplifies IndexedDB in Vue, offering reactive queries, offline-first capabilities, encryption, compression, and effortless integration.
-
-# IndexedDB Database in Vue Apps - The Power of RxDB
-
-Building robust, [offline-capable](../offline-first.md) Vue applications often involves leveraging browser storage solutions to manage data. IndexedDB is one such powerful tool, but its raw API can be challenging to work with directly. RxDB abstracts away much of IndexedDB's complexity, providing a more developer-friendly experience. In this article, we'll explore what IndexedDB is, why it's beneficial in Vue applications, the challenges of using plain IndexedDB, and how [RxDB](https://rxdb.info/) can simplify your development process while adding advanced features.
-
-## What is IndexedDB?
-
-[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) is a low-level API for storing significant amounts of structured data in the browser. It provides a transactional database system that can store key-value pairs, complex objects, and more. This storage engine is asynchronous and supports advanced data types, making it suitable for offline storage and complex web applications.
-
-
-
-
-
-## Why Use IndexedDB in Vue
-
-When building Vue applications, IndexedDB can play a crucial role in enhancing both performance and user experience. Here are some reasons to consider using IndexedDB:
-
-- **Offline-First / Local-First**: By storing data locally, your application remains functional even without an internet connection.
-- **Performance**: Using local data means [zero latency](./zero-latency-local-first.md) and no loading spinners, as data doesn't need to be fetched over a network.
-- **Easier Implementation**: Replicating all data to the client once is often simpler than implementing multiple endpoints for each user interaction.
-- **Scalability**: Local data reduces server load because queries run on the client side, decreasing server bandwidth and processing requirements.
-
-## Why To Not Use Plain IndexedDB
-
-While IndexedDB itself is powerful, its native API comes with several drawbacks for everyday application developers:
-
-- **Callback-Based API**: IndexedDB was originally designed around callbacks rather than modern Promises, making asynchronous code more cumbersome.
-- **Complexity**: IndexedDB is low-level, intended for library developers rather than for app developers who just want to store and query data easily.
-- **Basic Query API**: Its rudimentary query capabilities limit how you can efficiently perform complex queries. Libraries like RxDB offer more advanced querying and indexing.
-- **TypeScript Support**: Ensuring good [TypeScript support](../tutorials/typescript.md) with IndexedDB is challenging, especially when trying to maintain schema consistency.
-- **Lack of Observable API**: IndexedDB doesn't provide an observable API out of the box, making it hard to automatically update your Vue app in real time. RxDB solves this by enabling you to [observe queries](../rx-query.md#observe) or specific documents.
-- **Cross-Tab Communication**: Managing cross-tab updates in plain IndexedDB is difficult. RxDB handles this seamlessly - changes in one tab automatically affect observed data in others.
-- **Missing Advanced Features**: Features like [encryption](../encryption.md) or [compression](../key-compression.md) aren't built into IndexedDB, but they are available via RxDB.
-- **Limited Platform Support**: IndexedDB is browser-only. RxDB offers [swappable storages](../rx-storage.md) so you can reuse the same data layer code in mobile or desktop environments.
-
-
-
-
-
-
-
-## Set up RxDB in Vue
-
-Setting up RxDB with Vue is straightforward. It abstracts IndexedDB complexities and adds a layer of powerful features over it.
-
-### Installing RxDB
-
-First, install RxDB (and RxJS) from npm:
-
-```bash
-npm install rxdb rxjs --save
-```
-
-### Create a Database and Collections
-
-RxDB provides two main storage options:
-
-- The free [LocalStorage-based storage](../rx-storage-localstorage.md)
-- The premium plain [IndexedDB-based storage](../rx-storage-indexeddb.md), offering faster [performance](../rx-storage-performance.md)
-
-Below is an example of setting up a simple RxDB database using the localstorage-based storage in a Vue app:
-
-```ts
-// db.ts
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-export async function initDB() {
- const db = await createRxDatabase({
- name: 'heroesdb', // the name of the database
- storage: getRxStorageLocalstorage()
- });
-
- // Define your schema
- const heroSchema = {
- title: 'hero schema',
- version: 0,
- description: 'Describes a hero in your app',
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- name: {
- type: 'string'
- },
- power: {
- type: 'string'
- }
- },
- required: ['id', 'name']
- };
-
- // add collections
- await db.addCollections({
- heroes: {
- schema: heroSchema
- }
- });
-
- return db;
-}
-```
-
-### CRUD Operations
-
-Once your database is initialized, you can perform all CRUD operations:
-
-```ts
-// insert
-await db.heroes.insert({ id: '1', name: 'Iron Man', power: 'Genius-level intellect' });
-
-// bulk insert
-await db.heroes.bulkInsert([
- { id: '2', name: 'Thor', power: 'God of Thunder' },
- { id: '3', name: 'Hulk', power: 'Superhuman Strength' }
-]);
-
-// find and findOne
-const heroes = await db.heroes.find().exec();
-const ironMan = await db.heroes.findOne({ selector: { name: 'Iron Man' } }).exec();
-
-// update
-const doc = await db.heroes.findOne({ selector: { name: 'Hulk' } }).exec();
-await doc.update({ $set: { power: 'Unlimited Strength' } });
-
-// delete
-const thorDoc = await db.heroes.findOne({ selector: { name: 'Thor' } }).exec();
-await thorDoc.remove();
-```
-
-## Reactive Queries and Live Updates
-
-RxDB excels in providing reactive data capabilities, ideal for [real-time applications](./realtime-database.md). Subscribing to queries automatically updates your Vue components when underlying data changes - even across [browser](./browser-database.md) tabs.
-
-
-
-### Using RxJS Observables with Vue 3 Composition API
-
-Here's an example of a Vue component that subscribes to live data updates:
-
-```html
-
-
- Hero List
-
-
- {{ hero.name }} - {{ hero.power }}
-
-
-
-
-
-
-```
-
-This component subscribes to the collection's changes, [updating the UI](./optimistic-ui.md) automatically whenever the underlying data changes in any browser tab.
-
-### Using Vue Signals
-
-If you're exploring Vue's reactivity transforms or signals, RxDB also offers [custom reactivity factories](../reactivity.md) ([premium plugins](/premium/) are required). This allows queries to emit data as signals instead of traditional Observables.
-
-```ts
-const heroesSignal = db.heroes.find().$$; // $$ indicates a reactive result
-
-```
-With this, in your Vue template or script, you can directly read from heroesSignal()
-
-```html
-
-
- Hero List
-
-
-
- {{ hero.name }} - {{ hero.power }}
-
-
-
-
-```
-
-## Vue IndexedDB Example with RxDB
-
-A comprehensive example of using RxDB within a Vue application can be found in the [RxDB GitHub repository](https://github.com/pubkey/rxdb/tree/master/examples/vue). This repository contains sample applications, showcasing best practices and demonstrating how to integrate RxDB for various use cases.
-
-## Advanced RxDB Features
-RxDB offers many advanced features that extend beyond basic data storage:
-
-- [RxDB Replication](../replication.md): Synchronize local data with remote databases seamlessly.
-
-- [Data Migration](../migration-schema.md): Handle schema changes gracefully with automatic data migrations.
-
-- [Encryption](../encryption.md): Secure your data with built-in encryption capabilities.
-
-- [Compression](../key-compression.md): Optimize storage using key compression.
-
-## Limitations of IndexedDB
-
-While IndexedDB is powerful, it has some inherent limitations:
-
-- Performance: IndexedDB can be slow under certain conditions. Read more: [Slow IndexedDB](../slow-indexeddb.md)
-- [Storage Limits](./indexeddb-max-storage-limit.md): Browsers impose limits on how much data can be stored. See: [Browser storage limits](./localstorage-indexeddb-cookies-opfs-sqlite-wasm.md#storage-size-limits).
-
-## Alternatives to IndexedDB
-
-Depending on your application's requirements, there are [alternative storage solutions](../rx-storage.md) to consider:
-
-- **Origin Private File System (OPFS)**: A newer API that can offer better performance. RxDB supports OPFS as well. More info: [RxDB OPFS Storage](../rx-storage-opfs.md)
-- **SQLite**: Ideal for hybrid frameworks or Capacitor, offering native performance. Explore: [RxDB SQLite Storage](../rx-storage-sqlite.md)
-
-## Performance Comparison with Other Browser Storages
-Here is a performance overview of the various browser-based storage implementations of RxDB:
-
-
-
-## Follow Up
-- Learn how to use RxDB with the [RxDB Quickstart](../quickstart.md) for a guided introduction.
-- Check out the [RxDB GitHub repository](/code/) and leave a star ⭐ if you find it useful.
-
-By leveraging RxDB on top of IndexedDB, you can create highly responsive, offline-capable Vue applications without dealing with the low-level complexities of IndexedDB directly. With [reactive queries](../rx-query.md), seamless cross-tab communication, and powerful advanced features, RxDB becomes an invaluable tool in modern web development.
diff --git a/docs/articles/websockets-sse-polling-webrtc-webtransport.html b/docs/articles/websockets-sse-polling-webrtc-webtransport.html
deleted file mode 100644
index 7c3d25629c6..00000000000
--- a/docs/articles/websockets-sse-polling-webrtc-webtransport.html
+++ /dev/null
@@ -1,205 +0,0 @@
-
-
-
-
-
-WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport
-
For modern real-time web applications, the ability to send events from the server to the client is indispensable. This necessity has led to the development of several methods over the years, each with its own set of advantages and drawbacks. Initially, long-polling was the only option available. It was then succeeded by WebSockets, which offered a more robust solution for bidirectional communication. Following WebSockets, Server-Sent Events (SSE) provided a simpler method for one-way communication from server to client. Looking ahead, the WebTransport protocol promises to revolutionize this landscape even further by providing a more efficient, flexible, and scalable approach. For niche use cases, WebRTC might also be considered for server-client events.
-
This article aims to delve into these technologies, comparing their performance, highlighting their benefits and limitations, and offering recommendations for various use cases to help developers make informed decisions when building real-time web applications. It is a condensed summary of my gathered experience when I implemented the RxDB Sync Engine to be compatible with various backend technologies.
Long polling was the first "hack" to enable a server-client messaging method that can be used in browsers over HTTP. The technique emulates server push communications with normal XHR requests. Unlike traditional polling, where the client repeatedly requests data from the server at regular intervals, long polling establishes a connection to the server that remains open until new data is available. Once the server has new information, it sends the response to the client, and the connection is closed. Immediately after receiving the server's response, the client initiates a new request, and the process repeats. This method allows for more immediate data updates and reduces unnecessary network traffic and server load. However, it can still introduce delays in communication and is less efficient than other real-time technologies like WebSockets.
-
// long-polling in a JavaScript client
-function longPoll() {
- fetch('http://example.com/poll')
- .then(response => response.json())
- .then(data => {
- console.log("Received data:", data);
- longPoll(); // Immediately establish a new long polling request
- })
- .catch(error => {
- /**
- * Errors can appear in normal conditions when a
- * connection timeout is reached or when the client goes offline.
- * On errors we just restart the polling after some delay.
- */
- setTimeout(longPoll, 10000);
- });
-}
-longPoll(); // Initiate the long polling
-
Implementing long-polling on the client side is pretty simple, as shown in the code above. However on the backend there can be multiple difficulties to ensure the client receives all events and does not miss out updates when the client is currently reconnecting.
WebSockets provide a full-duplex communication channel over a single, long-lived connection between the client and server. This technology enables browsers and servers to exchange data without the overhead of HTTP request-response cycles, facilitating real-time data transfer for applications like live chat, gaming, or financial trading platforms. WebSockets represent a significant advancement over traditional HTTP by allowing both parties to send data independently once the connection is established, making it ideal for scenarios that require low latency and high-frequency updates.
-
// WebSocket in a JavaScript client
-const socket = new WebSocket('ws://example.com');
-
-socket.onopen = function(event) {
- console.log('Connection established');
- // Sending a message to the server
- socket.send('Hello Server!');
-};
-
-socket.onmessage = function(event) {
- console.log('Message from server:', event.data);
-};
-
While the basics of the WebSocket API are easy to use it has shown to be rather complex in production. A socket can loose connection and must be re-created accordingly. Especially detecting if a connection is still usable or not, can be very tricky. Mostly you would add a ping-and-pong heartbeat to ensure that the open connection is not closed.
-This complexity is why most people use a library on top of WebSockets like Socket.IO which handles all these cases and even provides fallbacks to long-polling if required.
Server-Sent Events (SSE) provide a standard way to push server updates to the client over HTTP. Unlike WebSockets, SSEs are designed exclusively for one-way communication from server to client, making them ideal for scenarios like live news feeds, sports scores, or any situation where the client needs to be updated in real time without sending data to the server.
-
You can think of Server-Sent-Events as a single HTTP request where the backend does not send the whole body at once, but instead keeps the connection open and trickles the answer by sending a single line each time an event has to be send to the client.
-
Creating a connection for receiving events with SSE is straightforward. On the client side in a browser, you initialize an EventSource instance with the URL of the server-side script that generates the events.
-
Listening for messages involves attaching event handlers directly to the EventSource instance. The API distinguishes between generic message events and named events, allowing for more structured communication. Here's how you can set it up in JavaScript:
-
// Connecting to the server-side event stream
-const evtSource = new EventSource("https://example.com/events");
-
-// Handling generic message events
-evtSource.onmessage = event => {
- console.log('got message: ' + event.data);
-};
-
In difference to WebSockets, an EventSource will automatically reconnect on connection loss.
-
On the server side, your script must set the Content-Type header to text/event-stream and format each message according to the SSE specification. This includes specifying event types, data payloads, and optional fields like event ID and retry timing.
-
Here's how you can set up a simple SSE endpoint in a Node.js Express app:
-
import express from 'express';
-const app = express();
-const PORT = process.env.PORT || 3000;
-
-app.get('/events', (req, res) => {
- res.writeHead(200, {
- 'Content-Type': 'text/event-stream',
- 'Cache-Control': 'no-cache',
- 'Connection': 'keep-alive',
- });
-
- const sendEvent = (data) => {
- // all message lines must be prefixed with 'data: '
- const formattedData = `data: ${JSON.stringify(data)}\n\n`;
- res.write(formattedData);
- };
-
- // Send an event every 2 seconds
- const intervalId = setInterval(() => {
- const message = {
- time: new Date().toTimeString(),
- message: 'Hello from the server!',
- };
- sendEvent(message);
- }, 2000);
-
- // Clean up when the connection is closed
- req.on('close', () => {
- clearInterval(intervalId);
- res.end();
- });
-});
-app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
WebTransport is a cutting-edge API designed for efficient, low-latency communication between web clients and servers. It leverages the HTTP/3 QUIC protocol to enable a variety of data transfer capabilities, such as sending data over multiple streams, in both reliable and unreliable manners, and even allowing data to be sent out of order. This makes WebTransport a powerful tool for applications requiring high-performance networking, such as real-time gaming, live streaming, and collaborative platforms. However, it's important to note that WebTransport is currently a working draft and has not yet achieved widespread adoption.
-As of now (March 2024), WebTransport is in a Working Draft and not widely supported. You cannot yet use WebTransport in the Safari browser and there is also no native support in Node.js. This limits its usability across different platforms and environments.
-
Even when WebTransport will become widely supported, its API is very complex to use and likely it would be something where people build libraries on top of WebTransport, not using it directly in an application's sourcecode.
WebRTC (Web Real-Time Communication) is an open-source project and API standard that enables real-time communication (RTC) capabilities directly within web browsers and mobile applications without the need for complex server infrastructure or the installation of additional plugins. It supports peer-to-peer connections for streaming audio, video, and data exchange between browsers. WebRTC is designed to work through NATs and firewalls, utilizing protocols like ICE, STUN, and TURN to establish a connection between peers.
-
While WebRTC is made to be used for client-client interactions, it could also be leveraged for server-client communication where the server just simulated being also a client. This approach only makes sense for niche use cases which is why in the following WebRTC will be ignored as an option.
-
The problem is that for WebRTC to work, you need a signaling-server anyway which would then again run over websockets, SSE or WebTransport. This defeats the purpose of using WebRTC as a replacement for these technologies.
Only WebSockets and WebTransport allow to send data in both directions so that you can receive server-data and send client-data over the same connection.
-
While it would also be possible with Long-Polling in theory, it is not recommended because sending "new" data to an existing long-polling connection would require
-to do an additional http-request anyway. So instead of doing that you can send data directly from the client to the server with an additional http-request without interrupting
-the long-polling connection.
-
Server-Sent-Events do not support sending any additional data to the server. You can only do the initial request, and even there you cannot send POST-like data in the http-body by default with the native EventSource API. Instead you have to put all data inside of the url parameters which is considered a bad practice for security because credentials might leak into server logs, proxies and caches. To fix this problem, RxDB for example uses the eventsource polyfill instead of the native EventSource API. This library adds additional functionality like sending custom http headers. Also there is this library from microsoft which allows to send body data and use POST requests instead of GET.
Most modern browsers allow six connections per domain () which limits the usability of all steady server-to-client messaging methods. The limitation of six connections is even shared across browser tabs so when you open the same page in multiple tabs, they would have to shared the six-connection-pool with each other. This limitation is part of the HTTP/1.1-RFC (which even defines a lower number of only two connections).
-
-
Quote From RFC 2616 - Section 8.1.4: "Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion."
-
-
While that policy makes sense to prevent website owners from using their visitors to D-DOS other websites, it can be a big problem when multiple connections are required to handle server-client communication for legitimate use cases. To workaround the limitation you have to use HTTP/2 or HTTP/3 with which the browser will only open a single connection per domain and then use multiplexing to run all data through a single connection. While this gives you a virtually infinity amount of parallel connections, there is a SETTINGS_MAX_CONCURRENT_STREAMS setting which limits the actually connections amount. The default is 100 concurrent streams for most configurations.
-
In theory the connection limit could also be increased by the browser, at least for specific APIs like EventSource, but the issues have been marked as "won't fix" by chromium and firefox.
-
Lower the amount of connections in Browser Apps
When you build a browser application, you have to assume that your users will use the app not only once, but in multiple browser tabs in parallel.
-By default you likely will open one server-stream-connection per tab which is often not necessary at all. Instead you open only a single connection and shared it between tabs, no matter how many tabs are open. RxDB does that with the LeaderElection from the broadcast-channel npm package to only have one stream of replication between server and clients. You can use that package standalone (without RxDB) for any type of application.
In the context of mobile applications running on operating systems like Android and iOS, maintaining open connections, such as those used for WebSockets and the others, poses a significant challenge. Mobile operating systems are designed to automatically move applications into the background after a certain period of inactivity, effectively closing any open connections. This behavior is a part of the operating system's resource management strategy to conserve battery and optimize performance. As a result, developers often rely on mobile push notifications as an efficient and reliable method to send data from servers to clients. Push notifications allow servers to alert the application of new data, prompting an action or update, without the need for a persistent open connection.
From consulting many RxDB users, it was shown that in enterprise environments (aka "at work") it is often hard to implement a WebSocket server into the infrastructure because many proxies and firewalls block non-HTTP connections. Therefore using the Server-Sent-Events provides and easier way of enterprise integration. Also long-polling uses only plain HTTP-requests and might be an option.
Comparing the performance of WebSockets, Server-Sent Events (SSE), Long-Polling and WebTransport directly involves evaluating key aspects such as latency, throughput, server load, and scalability under various conditions.
-
First lets look at the raw numbers. A good performance comparison can be found in this repo which tests the messages times in a Go Lang server implementation. Here we can see that the performance of WebSockets, WebRTC and WebTransport are comparable:
-
-
note
Remember that WebTransport is a pretty new technology based on the also new HTTP/3 protocol. In the future (after March 2024) there might be more performance optimizations. Also WebTransport is optimized to use less power which metric is not tested.
-
Lets also compare the Latency, the throughput and the scalability:
WebSockets: Offers the lowest latency due to its full-duplex communication over a single, persistent connection. Ideal for real-time applications where immediate data exchange is critical.
-
Server-Sent Events: Also provides low latency for server-to-client communication but cannot natively send messages back to the server without additional HTTP requests.
-
Long-Polling: Incurs higher latency as it relies on establishing new HTTP connections for each data transmission, making it less efficient for real-time updates. Also it can occur that the server wants to send an event when the client is still in the process of opening a new connection. In these cases the latency would be significantly larger.
-
WebTransport: Promises to offer low latency similar to WebSockets, with the added benefits of leveraging the HTTP/3 protocol for more efficient multiplexing and congestion control.
WebSockets: Capable of high throughput due to its persistent connection, but throughput can suffer from backpressure where the client cannot process data as fast as the server is capable of sending it.
-
Server-Sent Events: Efficient for broadcasting messages to many clients with less overhead than WebSockets, leading to potentially higher throughput for unidirectional server-to-client communication.
-
Long-Polling: Generally offers lower throughput due to the overhead of frequently opening and closing connections, which consumes more server resources.
-
WebTransport: Expected to support high throughput for both unidirectional and bidirectional streams within a single connection, outperforming WebSockets in scenarios requiring multiple streams.
WebSockets: Maintaining a large number of WebSocket connections can significantly increase server load, potentially affecting scalability for applications with many users.
-
Server-Sent Events: More scalable for scenarios that primarily require updates from server to client, as it uses less connection overhead than WebSockets because it uses "normal" HTTP request without things like protocol updates that have to be run with WebSockets.
-
Long-Polling: The least scalable due to the high server load generated by frequent connection establishment, making it suitable only as a fallback mechanism.
-
WebTransport: Designed to be highly scalable, benefiting from HTTP/3's efficiency in handling connections and streams, potentially reducing server load compared to WebSockets and SSE.
In the landscape of server-client communication technologies, each has its distinct advantages and use case suitability. Server-Sent Events (SSE) emerge as the most straightforward option to implement, leveraging the same HTTP/S protocols as traditional web requests, thereby circumventing corporate firewall restrictions and other technical problems that can appear with other protocols. They are easily integrated into Node.js and other server frameworks, making them an ideal choice for applications requiring frequent server-to-client updates, such as news feeds, stock tickers, and live event streaming.
-
On the other hand, WebSockets excel in scenarios demanding ongoing, two-way communication. Their ability to support continuous interaction makes them the prime choice for browser games, chat applications, and live sports updates.
-
However, WebTransport, despite its potential, faces adoption challenges. It is not widely supported by server frameworks including Node.js and lacks compatibility with safari. Moreover, its reliance on HTTP/3 further limits its immediate applicability because many WebServers like nginx only have experimental HTTP/3 support. While promising for future applications with its support for both reliable and unreliable data transmission, WebTransport is not yet a viable option for most use cases.
-
Long-Polling, once a common technique, is now largely outdated due to its inefficiency and the high overhead of repeatedly establishing new HTTP connections. Although it may serve as a fallback in environments lacking support for WebSockets or SSE, its use is generally discouraged due to significant performance limitations.
When a client is connecting, reconnecting or offline, it can miss out events that happened on the server but could not be streamed to the client.
-This missed out events are not relevant when the server is streaming the full content each time anyway, like on a live updating stock ticker.
-But when the backend is made to stream partial results, you have to account for missed out events. Fixing that on the backend scales pretty bad because the backend would have to remember for each client which events have been successfully send already. Instead this should be implemented with client side logic.
-
The RxDB Sync Engine for example uses two modes of operation for that. One is the checkpoint iteration mode where normal http requests are used to iterate over backend data, until the client is in sync again. Then it can switch to event observation mode where updates from the realtime-stream are used to keep the client in sync. Whenever a client disconnects or has any error, the replication shortly switches to checkpoint iteration mode until the client is in sync again. This method accounts for missed out events and ensures that clients can always sync to the exact equal state of the server.
There are many known problems with company infrastructure when using any of the streaming technologies. Proxies and firewall can block traffic or unintentionally break requests and responses. Whenever you implement a realtime app in such an infrastructure, make sure you first test out if the technology itself works for you.
-
-
\ No newline at end of file
diff --git a/docs/articles/websockets-sse-polling-webrtc-webtransport.md b/docs/articles/websockets-sse-polling-webrtc-webtransport.md
deleted file mode 100644
index ab3e79eba45..00000000000
--- a/docs/articles/websockets-sse-polling-webrtc-webtransport.md
+++ /dev/null
@@ -1,252 +0,0 @@
-# WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport
-
-> Learn the unique benefits and pitfalls of each real-time tech. Make informed decisions on WebSockets, SSE, Polling, WebRTC, and WebTransport.
-
-# WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport
-
-For modern real-time web applications, the ability to send events from the server to the client is indispensable. This necessity has led to the development of several methods over the years, each with its own set of advantages and drawbacks. Initially, [long-polling](#what-is-long-polling) was the only option available. It was then succeeded by [WebSockets](#what-are-websockets), which offered a more robust solution for bidirectional communication. Following WebSockets, [Server-Sent Events (SSE)](#what-are-server-sent-events) provided a simpler method for one-way communication from server to client. Looking ahead, the [WebTransport](#what-is-the-webtransport-api) protocol promises to revolutionize this landscape even further by providing a more efficient, flexible, and scalable approach. For niche use cases, [WebRTC](#what-is-webrtc) might also be considered for server-client events.
-
-This article aims to delve into these technologies, comparing their performance, highlighting their benefits and limitations, and offering recommendations for various use cases to help developers make informed decisions when building real-time web applications. It is a condensed summary of my gathered experience when I implemented the [RxDB Sync Engine](../replication.md) to be compatible with various backend technologies.
-
-
-
-
-
-
-
-### What is Long Polling?
-
-Long polling was the first "hack" to enable a server-client messaging method that can be used in browsers over HTTP. The technique emulates server push communications with normal XHR requests. Unlike traditional polling, where the client repeatedly requests data from the server at regular intervals, long polling establishes a connection to the server that remains open until new data is available. Once the server has new information, it sends the response to the client, and the connection is closed. Immediately after receiving the server's response, the client initiates a new request, and the process repeats. This method allows for more immediate data updates and reduces unnecessary network traffic and server load. However, it can still introduce delays in communication and is less efficient than other real-time technologies like WebSockets.
-
-```js
-// long-polling in a JavaScript client
-function longPoll() {
- fetch('http://example.com/poll')
- .then(response => response.json())
- .then(data => {
- console.log("Received data:", data);
- longPoll(); // Immediately establish a new long polling request
- })
- .catch(error => {
- /**
- * Errors can appear in normal conditions when a
- * connection timeout is reached or when the client goes offline.
- * On errors we just restart the polling after some delay.
- */
- setTimeout(longPoll, 10000);
- });
-}
-longPoll(); // Initiate the long polling
-```
-
-Implementing long-polling on the client side is pretty simple, as shown in the code above. However on the backend there can be multiple difficulties to ensure the client receives all events and does not miss out updates when the client is currently reconnecting.
-
-### What are WebSockets?
-
-[WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket?retiredLocale=de) provide a full-duplex communication channel over a single, long-lived connection between the client and server. This technology enables browsers and servers to exchange data without the overhead of HTTP request-response cycles, facilitating real-time data transfer for applications like live chat, gaming, or financial trading platforms. WebSockets represent a significant advancement over traditional HTTP by allowing both parties to send data independently once the connection is established, making it ideal for scenarios that require low latency and high-frequency updates.
-
-```js
-// WebSocket in a JavaScript client
-const socket = new WebSocket('ws://example.com');
-
-socket.onopen = function(event) {
- console.log('Connection established');
- // Sending a message to the server
- socket.send('Hello Server!');
-};
-
-socket.onmessage = function(event) {
- console.log('Message from server:', event.data);
-};
-```
-
-While the basics of the WebSocket API are easy to use it has shown to be rather complex in production. A socket can loose connection and must be re-created accordingly. Especially detecting if a connection is still usable or not, can be very tricky. Mostly you would add a [ping-and-pong](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#pings_and_pongs_the_heartbeat_of_websockets) heartbeat to ensure that the open connection is not closed.
-This complexity is why most people use a library on top of WebSockets like [Socket.IO](https://socket.io/) which handles all these cases and even provides fallbacks to long-polling if required.
-
-### What are Server-Sent-Events?
-
-Server-Sent Events (SSE) provide a standard way to push server updates to the client over HTTP. Unlike WebSockets, SSEs are designed exclusively for one-way communication from server to client, making them ideal for scenarios like live news feeds, sports scores, or any situation where the client needs to be updated in real time without sending data to the server.
-
-You can think of Server-Sent-Events as a single HTTP request where the backend does not send the whole body at once, but instead keeps the connection open and trickles the answer by sending a single line each time an event has to be send to the client.
-
-Creating a connection for receiving events with SSE is straightforward. On the client side in a browser, you initialize an [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) instance with the URL of the server-side script that generates the events.
-
-Listening for messages involves attaching event handlers directly to the EventSource instance. The API distinguishes between generic message events and named events, allowing for more structured communication. Here's how you can set it up in JavaScript:
-
-```js
-// Connecting to the server-side event stream
-const evtSource = new EventSource("https://example.com/events");
-
-// Handling generic message events
-evtSource.onmessage = event => {
- console.log('got message: ' + event.data);
-};
-```
-
-In difference to WebSockets, an EventSource will automatically reconnect on connection loss.
-
-On the server side, your script must set the `Content-Type` header to `text/event-stream` and format each message according to the [SSE specification](https://www.w3.org/TR/2012/WD-eventsource-20120426/). This includes specifying event types, data payloads, and optional fields like event ID and retry timing.
-
-Here's how you can set up a simple SSE endpoint in a Node.js Express app:
-
-```ts
-import express from 'express';
-const app = express();
-const PORT = process.env.PORT || 3000;
-
-app.get('/events', (req, res) => {
- res.writeHead(200, {
- 'Content-Type': 'text/event-stream',
- 'Cache-Control': 'no-cache',
- 'Connection': 'keep-alive',
- });
-
- const sendEvent = (data) => {
- // all message lines must be prefixed with 'data: '
- const formattedData = `data: ${JSON.stringify(data)}\n\n`;
- res.write(formattedData);
- };
-
- // Send an event every 2 seconds
- const intervalId = setInterval(() => {
- const message = {
- time: new Date().toTimeString(),
- message: 'Hello from the server!',
- };
- sendEvent(message);
- }, 2000);
-
- // Clean up when the connection is closed
- req.on('close', () => {
- clearInterval(intervalId);
- res.end();
- });
-});
-app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
-```
-
-### What is the WebTransport API?
-
-WebTransport is a cutting-edge API designed for efficient, low-latency communication between web clients and servers. It leverages the [HTTP/3 QUIC protocol](https://en.wikipedia.org/wiki/HTTP/3) to enable a variety of data transfer capabilities, such as sending data over multiple streams, in both reliable and unreliable manners, and even allowing data to be sent out of order. This makes WebTransport a powerful tool for applications requiring high-performance networking, such as real-time gaming, live streaming, and collaborative platforms. However, it's important to note that WebTransport is currently a working draft and has not yet achieved widespread adoption.
-As of now (March 2024), WebTransport is in a [Working Draft](https://w3c.github.io/webtransport/) and not widely supported. You cannot yet use WebTransport in the [Safari browser](https://caniuse.com/webtransport) and there is also no native support [in Node.js](https://github.com/w3c/webtransport/issues/511). This limits its usability across different platforms and environments.
-
-Even when WebTransport will become widely supported, its API is very complex to use and likely it would be something where people build libraries on top of WebTransport, not using it directly in an application's sourcecode.
-
-### What is WebRTC?
-
-[WebRTC](https://webrtc.org/) (Web Real-Time Communication) is an open-source project and API standard that enables real-time communication (RTC) capabilities directly within web browsers and mobile applications without the need for complex server infrastructure or the installation of additional plugins. It supports peer-to-peer connections for streaming audio, video, and data exchange between browsers. [WebRTC](../replication-webrtc.md) is designed to work through NATs and firewalls, utilizing protocols like ICE, STUN, and TURN to establish a connection between peers.
-
-While WebRTC is made to be used for client-client interactions, it could also be leveraged for server-client communication where the server just simulated being also a client. This approach only makes sense for niche use cases which is why in the following WebRTC will be ignored as an option.
-
-The problem is that for WebRTC to work, you need a signaling-server anyway which would then again run over websockets, SSE or WebTransport. This defeats the purpose of using WebRTC as a replacement for these technologies.
-
-## Limitations of the technologies
-
-### Sending Data in both directions
-
-Only WebSockets and WebTransport allow to send data in both directions so that you can receive server-data and send client-data over the same connection.
-
-While it would also be possible with **Long-Polling** in theory, it is not recommended because sending "new" data to an existing long-polling connection would require
-to do an additional http-request anyway. So instead of doing that you can send data directly from the client to the server with an additional http-request without interrupting
-the long-polling connection.
-
-**Server-Sent-Events** do not support sending any additional data to the server. You can only do the initial request, and even there you cannot send POST-like data in the http-body by default with the native [EventSource API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). Instead you have to put all data inside of the url parameters which is considered a bad practice for security because credentials might leak into server logs, proxies and caches. To fix this problem, [RxDB](https://rxdb.info/) for example uses the [eventsource polyfill](https://github.com/EventSource/eventsource) instead of the native `EventSource API`. This library adds additional functionality like sending **custom http headers**. Also there is [this library](https://github.com/Azure/fetch-event-source) from microsoft which allows to send body data and use `POST` requests instead of `GET`.
-
-### 6-Requests per Domain Limit
-
-Most modern browsers allow six connections per domain () which limits the usability of all steady server-to-client messaging methods. The limitation of six connections is even shared across browser tabs so when you open the same page in multiple tabs, they would have to shared the six-connection-pool with each other. This limitation is part of the HTTP/1.1-RFC (which even defines a lower number of only two connections).
-
-> Quote From [RFC 2616 - Section 8.1.4](https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4): "Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than **2 connections** with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion."
-
-While that policy makes sense to prevent website owners from using their visitors to D-DOS other websites, it can be a big problem when multiple connections are required to handle server-client communication for legitimate use cases. To workaround the limitation you have to use HTTP/2 or HTTP/3 with which the browser will only open a single connection per domain and then use multiplexing to run all data through a single connection. While this gives you a virtually infinity amount of parallel connections, there is a [SETTINGS_MAX_CONCURRENT_STREAMS](https://www.rfc-editor.org/rfc/rfc7540#section-6.5.2) setting which limits the actually connections amount. The default is 100 concurrent streams for most configurations.
-
-In theory the connection limit could also be increased by the browser, at least for specific APIs like EventSource, but the issues have been marked as "won't fix" by [chromium](https://issues.chromium.org/issues/40329530) and [firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=906896).
-
-:::note Lower the amount of connections in Browser Apps
-When you build a browser application, you have to assume that your users will use the app not only once, but in multiple browser tabs in parallel.
-By default you likely will open one server-stream-connection per tab which is often not necessary at all. Instead you open only a single connection and shared it between tabs, no matter how many tabs are open. [RxDB](https://rxdb.info/) does that with the [LeaderElection](../leader-election.md) from the [broadcast-channel npm package](https://github.com/pubkey/broadcast-channel) to only have one stream of replication between server and clients. You can use that package standalone (without RxDB) for any type of application.
-:::
-
-### Connections are not kept open on mobile apps
-
-In the context of mobile applications running on operating systems like Android and iOS, maintaining open connections, such as those used for WebSockets and the others, poses a significant challenge. Mobile operating systems are designed to automatically move applications into the background after a certain period of inactivity, effectively closing any open connections. This behavior is a part of the operating system's resource management strategy to conserve battery and optimize performance. As a result, developers often rely on **mobile push notifications** as an efficient and reliable method to send data from servers to clients. Push notifications allow servers to alert the application of new data, prompting an action or update, without the need for a persistent open connection.
-
-### Proxies and Firewalls
-
-From consulting many [RxDB](https://rxdb.info/) users, it was shown that in enterprise environments (aka "at work") it is often hard to implement a WebSocket server into the infrastructure because many proxies and firewalls block non-HTTP connections. Therefore using the Server-Sent-Events provides and easier way of enterprise integration. Also long-polling uses only plain HTTP-requests and might be an option.
-
-
-
-
-
-
-
-## Performance Comparison
-
-Comparing the performance of WebSockets, Server-Sent Events (SSE), Long-Polling and WebTransport directly involves evaluating key aspects such as latency, throughput, server load, and scalability under various conditions.
-
-First lets look at the raw numbers. A good performance comparison can be found in [this repo](https://github.com/Sh3b0/realtime-web?tab=readme-ov-file#demos) which tests the messages times in a [Go Lang](https://go.dev/) server implementation. Here we can see that the performance of WebSockets, WebRTC and WebTransport are comparable:
-
-
-
-
-
-
-
-:::note
-Remember that WebTransport is a pretty new technology based on the also new HTTP/3 protocol. In the future (after March 2024) there might be more performance optimizations. Also WebTransport is optimized to use less power which metric is not tested.
-:::
-
-Lets also compare the Latency, the throughput and the scalability:
-
-### Latency
-- **WebSockets**: Offers the lowest latency due to its full-duplex communication over a single, persistent connection. Ideal for real-time applications where immediate data exchange is critical.
-- **Server-Sent Events**: Also provides low latency for server-to-client communication but cannot natively send messages back to the server without additional HTTP requests.
-- **Long-Polling**: Incurs higher latency as it relies on establishing new HTTP connections for each data transmission, making it less efficient for real-time updates. Also it can occur that the server wants to send an event when the client is still in the process of opening a new connection. In these cases the latency would be significantly larger.
-- **WebTransport**: Promises to offer low latency similar to WebSockets, with the added benefits of leveraging the HTTP/3 protocol for more efficient multiplexing and congestion control.
-
-### Throughput
-- **WebSockets**: Capable of high throughput due to its persistent connection, but throughput can suffer from [backpressure](https://chromestatus.com/feature/5189728691290112) where the client cannot process data as fast as the server is capable of sending it.
-- **Server-Sent Events**: Efficient for broadcasting messages to many clients with less overhead than WebSockets, leading to potentially higher throughput for unidirectional server-to-client communication.
-- **Long-Polling**: Generally offers lower throughput due to the overhead of frequently opening and closing connections, which consumes more server resources.
-- **WebTransport**: Expected to support high throughput for both unidirectional and bidirectional streams within a single connection, outperforming WebSockets in scenarios requiring multiple streams.
-
-### Scalability and Server Load
-- **WebSockets**: Maintaining a large number of WebSocket connections can significantly increase server load, potentially affecting scalability for applications with many users.
-- **Server-Sent Events**: More scalable for scenarios that primarily require updates from server to client, as it uses less connection overhead than WebSockets because it uses "normal" HTTP request without things like [protocol updates](https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism) that have to be run with WebSockets.
-- **Long-Polling**: The least scalable due to the high server load generated by frequent connection establishment, making it suitable only as a fallback mechanism.
-- **WebTransport**: Designed to be highly scalable, benefiting from HTTP/3's efficiency in handling connections and streams, potentially reducing server load compared to WebSockets and SSE.
-
-## Recommendations and Use-Case Suitability
-
-In the landscape of server-client communication technologies, each has its distinct advantages and use case suitability. **Server-Sent Events** (SSE) emerge as the most straightforward option to implement, leveraging the same HTTP/S protocols as traditional web requests, thereby circumventing corporate firewall restrictions and other technical problems that can appear with other protocols. They are easily integrated into Node.js and other server frameworks, making them an ideal choice for applications requiring frequent server-to-client updates, such as news feeds, stock tickers, and live event streaming.
-
-On the other hand, **WebSockets** excel in scenarios demanding ongoing, two-way communication. Their ability to support continuous interaction makes them the prime choice for browser games, chat applications, and live sports updates.
-
-However, **WebTransport**, despite its potential, faces adoption challenges. It is not widely supported by server frameworks [including Node.js](https://github.com/w3c/webtransport/issues/511) and lacks compatibility with [safari](https://caniuse.com/webtransport). Moreover, its reliance on HTTP/3 further limits its immediate applicability because many WebServers like nginx only have [experimental](https://nginx.org/en/docs/quic.html) HTTP/3 support. While promising for future applications with its support for both reliable and unreliable data transmission, WebTransport is not yet a viable option for most use cases.
-
-**Long-Polling**, once a common technique, is now largely outdated due to its inefficiency and the high overhead of repeatedly establishing new HTTP connections. Although it may serve as a fallback in environments lacking support for WebSockets or SSE, its use is generally discouraged due to significant performance limitations.
-
-## Known Problems
-
-For all of the realtime streaming technologies, there are known problems. When you build anything on top of them, keep these in mind.
-
-### A client can miss out events when reconnecting
-
-When a client is connecting, reconnecting or offline, it can miss out events that happened on the server but could not be streamed to the client.
-This missed out events are not relevant when the server is streaming the full content each time anyway, like on a live updating stock ticker.
-But when the backend is made to stream partial results, you have to account for missed out events. Fixing that on the backend scales pretty bad because the backend would have to remember for each client which events have been successfully send already. Instead this should be implemented with client side logic.
-
-The [RxDB Sync Engine](../replication.md) for example uses two modes of operation for that. One is the [checkpoint iteration mode](../replication.md#checkpoint-iteration) where normal http requests are used to iterate over backend data, until the client is in sync again. Then it can switch to [event observation mode](../replication.md#event-observation) where updates from the realtime-stream are used to keep the client in sync. Whenever a client disconnects or has any error, the replication shortly switches to [checkpoint iteration mode](../replication.md#checkpoint-iteration) until the client is in sync again. This method accounts for missed out events and ensures that clients can always sync to the exact equal state of the server.
-
-### Company firewalls can cause problems
-
-There are many known problems with company infrastructure when using any of the streaming technologies. Proxies and firewall can block traffic or unintentionally break requests and responses. Whenever you implement a realtime app in such an infrastructure, make sure you first test out if the technology itself works for you.
-
-## Follow Up
-
-- Check out the [hackernews discussion of this article](https://news.ycombinator.com/item?id=39745993)
-- Shared/Like my [announcement tweet](https://twitter.com/rxdbjs/status/1769507055298064818)
-- Learn how to use Server-Sent-Events to [replicate a client side RxDB database with your backend](../replication-http.md#pullstream-for-ongoing-changes).
-- Learn how to use RxDB with the [RxDB Quickstart](../quickstart.md)
-- Check out the [RxDB github repo](https://github.com/pubkey/rxdb) and leave a star ⭐
diff --git a/docs/articles/zero-latency-local-first.html b/docs/articles/zero-latency-local-first.html
deleted file mode 100644
index 0961aedec52..00000000000
--- a/docs/articles/zero-latency-local-first.html
+++ /dev/null
@@ -1,214 +0,0 @@
-
-
-
-
-
-Zero Latency Local First Apps with RxDB – Sync, Encryption and Compression | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Zero Latency Local First Apps with RxDB – Sync, Encryption and Compression
-
Creating a zero-latency local first application involves ensuring that most (if not all) user interactions occur instantaneously, without waiting on remote network responses. This design drastically enhances user experience, allowing apps to remain responsive and functional even when offline or experiencing poor connectivity. As developers, we can achieve this by storing data locally on the client and synchronizing it to the backend in the background. RxDB (Reactive Database) offers a comprehensive set of features - covering replication, offline support, encryption, compression, conflict handling, and more - that make it straightforward to build such high-performing apps.
In a traditional architecture, each user action triggers requests to a server for reads or writes. Despite network optimizations, unavoidable latencies can delay responses and disrupt the user flow. By contrast, a local first model maintains data in the client's environment (browser, mobile, desktop), drastically reducing user-perceived delays. Once the user re-connects or resumes activity online, changes propagate automatically to the server, eliminating manual synchronization overhead.
-
-
Instant Responsiveness: Because user actions (queries, updates, etc.) happen against a local datastore, UI updates do not wait on round-trip times.
-
Offline Operation: Apps can continue to read and write data, even when there is zero connectivity.
-
Reduced Backend Load: Instead of flooding the server with small requests, replication can combine and push or pull changes in batches.
-
Simplified Caching: Instead of implementing multi-layer caching, local first transforms your data layer into a reliable, quickly accessible store for all user actions.
RxDB is a JavaScript-based NoSQL database designed for offline-first and real-time replication scenarios. It supports a range of environments - browsers (IndexedDB or OPFS), mobile (Ionic, React Native), Electron, Node.js - and is built around:
-
-
Reactive Queries that trigger UI updates upon data changes
-
Schema-based NoSQL Documents for flexible but robust data models
RxDB's replication logic revolves around pulling down remote changes and pushing up local modifications. It maintains a checkpoint-based mechanism, so only new or updated documents flow between the client and server, reducing bandwidth usage and latency. This ensures:
-
-
Live Data: Queries automatically reflect server-side changes once they arrive locally.
-
Background Updates: No manual polling needed; replication streams or intervals handle synchronization.
-
Conflict Handling (see below) ensures data merges gracefully when multiple clients edit the same document offline.
RxDB's flexible replication system lets you connect to different backends or even peer-to-peer networks. There are official plugins for CouchDB, Firestore, GraphQL, WebRTC, and more. Many developers create a custom HTTP replication to work with their existing REST-based backend, ensuring a painless integration that doesn't require adopting an entirely new server infrastructure.
import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-async function initZeroLocalDB() {
- // Create a local RxDB instance using localstorage-based storage
- const db = await createRxDatabase({
- name: 'myZeroLocalDB',
- storage: getRxStorageLocalstorage(),
- // optional: password for encryption if needed
- });
-
- // Define one or more collections
- await db.addCollections({
- tasks: {
- schema: {
- title: 'task schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string', maxLength: 100 },
- title: { type: 'string' },
- done: { type: 'boolean' }
- }
- }
- }
- });
-
- // Reactive query - automatically updates on local or remote changes
- db.tasks
- .find()
- .$ // returns an RxJS Observable
- .subscribe(allTasks => {
- console.log('All tasks updated:', allTasks);
- });
-
- return db;
-}
-
When offline, reads and writes to db.tasks happen locally with near-zero delay. Once connectivity resumes, changes sync to the server automatically (if replication is configured).
import { replicateRxCollection } from 'rxdb/plugins/replication';
-
-async function syncLocalTasks(db) {
- replicateRxCollection({
- collection: db.tasks,
- replicationIdentifier: 'sync-tasks',
- // Define how to pull server documents and push local documents
- pull: {
- handler: async (lastCheckpoint, batchSize) => {
- // logic to retrieve updated tasks from the server since lastCheckpoint
- },
- },
- push: {
- handler: async (docs) => {
- // logic to post local changes to the server
- },
- },
- live: true, // continuously replicate
- retryTime: 5000, // retry on errors or disconnections
- });
-}
-
This replication seamlessly merges server-side and client-side changes. Your app remains responsive throughout, regardless of the network status.
A local first approach, especially with RxDB, naturally supports an optimistic UI pattern. Because writes occur on the client, you can instantly reflect changes in the interface as soon as the user performs an action - no need to wait for server confirmation. For example, when a user updates a task document to done: true, the UI can re-render immediately with that new state. This even works across multiple browser tabs.
-
-
If a server conflict arises later during replication, RxDB's conflict handling logic determines which changes to keep, and the UI can be updated accordingly. This is far more efficient than blocking the user or displaying a spinner while the backend processes the request.
In local first models, conflicts emerge if multiple devices or clients edit the same document while offline. RxDB tracks document revisions so you can detect collisions and merge them effectively. By default, RxDB uses a last-write-wins approach, but developers can override it with a custom conflict handler. This provides fine-grained control - like merging partial fields, storing revision histories, or prompting users for resolution. Proper conflict handling keeps distributed data consistent across your entire system.
Over time, apps evolve - new fields, changed field types, or altered indexes. RxDB allows incremental schema migrations so you can upgrade a user's local data from one schema version to another. You might, for instance, rename a property or transform data formats. Once you define your migration strategy, RxDB automatically applies it upon app initialization, ensuring the local database's structure aligns with your latest codebase.
When storing data locally, you may handle user-sensitive information like PII (Personal Identifiable Information) or financial details. RxDB supports on-device encryption to protect fields. For example, you can define:
Local data can expand quickly, especially for large documents or repeated key names. RxDB's key compression feature replaces verbose field names with shorter tokens, decreasing storage usage and speeding up replication. You enable it by adding keyCompression: true to your collection schema:
By choosing a suitable storage layer, you can adapt your zero-latency local first design to any runtime - web, mobile, or server-like contexts in Node.js.
Performant local data operations are crucial for a zero-latency experience. According to the RxDB storage performance overview, differences in underlying storages can significantly impact throughput and latency. For instance, IndexedDB typically performs well across modern browsers, OPFS offers improved throughput in supporting browsers, and SQLite storage (a premium plugin) often delivers near-native speed for mobile or desktop.
In a browser environment, you can move database operations into a Web Worker using the Worker RxStorage plugin. This approach lets you keep heavy data processing off the main thread, ensuring the UI remains smooth and responsive. Complex queries or large write operations no longer cause stuttering in the user interface.
For large datasets or high concurrency, advanced techniques like sharding collections across multiple storages or leveraging a memory-mapped variant can further boost performance. By splitting data into smaller subsets or streaming it only as needed, you can scale to handle complex usage scenarios without compromising on the zero-latency user experience.
Join the RxDB Community on GitHub and Discord to share insights, file issues, and learn from other developers building zero-latency solutions.
-
-
-
By integrating RxDB into your stack, you achieve millisecond interactions, full offline capabilities, secure data at rest, and minimal overhead for large or distributed teams. This zero-latency local first architecture is the future of modern software - delivering a fluid, always-available user experience without overcomplicating the developer workflow.
-
-
\ No newline at end of file
diff --git a/docs/articles/zero-latency-local-first.md b/docs/articles/zero-latency-local-first.md
deleted file mode 100644
index 7abb4e58f57..00000000000
--- a/docs/articles/zero-latency-local-first.md
+++ /dev/null
@@ -1,228 +0,0 @@
-# Zero Latency Local First Apps with RxDB – Sync, Encryption and Compression
-
-> Build blazing-fast, zero-latency local first apps with RxDB. Gain instant UI responses, robust offline capabilities, end-to-end encryption, and data compression for streamlined performance.
-
-# Zero Latency Local First Apps with RxDB – Sync, Encryption and Compression
-
-Creating a **zero-latency local first** application involves ensuring that most (if not all) user interactions occur instantaneously, without waiting on remote network responses. This design drastically enhances user experience, allowing apps to remain responsive and functional even when offline or experiencing poor connectivity. As developers, we can achieve this by storing data **locally on the client** and synchronizing it to the backend in the background. **RxDB** (Reactive Database) offers a comprehensive set of features - covering replication, offline support, encryption, compression, conflict handling, and more - that make it straightforward to build such high-performing apps.
-
-
-
-## Why Zero Latency with a Local First Approach?
-
-In a traditional architecture, each user action triggers requests to a server for reads or writes. Despite network optimizations, unavoidable latencies can delay responses and disrupt the user flow. By contrast, a local first model maintains data in the client's environment (browser, mobile, desktop), drastically reducing user-perceived delays. Once the user re-connects or resumes activity online, changes propagate automatically to the server, eliminating manual synchronization overhead.
-
-1. **Instant Responsiveness**: Because user actions (queries, updates, etc.) happen against a local datastore, UI updates do not wait on round-trip times.
-2. **Offline Operation**: Apps can continue to read and write data, even when there is zero connectivity.
-3. **Reduced Backend Load**: Instead of flooding the server with small requests, replication can combine and push or pull changes in batches.
-4. **Simplified Caching**: Instead of implementing multi-layer caching, local first transforms your data layer into a reliable, quickly accessible store for all user actions.
-
-
-
-
-
-
-
-## RxDB: Your Key to Zero-Latency Local First Apps
-
-**RxDB** is a JavaScript-based NoSQL database designed for offline-first and real-time replication scenarios. It supports a range of environments - browsers (IndexedDB or OPFS), mobile ([Ionic](./ionic-storage.md), [React Native](../react-native-database.md)), [Electron](../electron-database.md), Node.js - and is built around:
-
-- **Reactive Queries** that trigger UI updates upon data changes
-- **Schema-based NoSQL Documents** for flexible but robust data models
-- [Advanced Sync Engine](../replication.md): to synchronize with diverse backends
-- **Encryption** for secure data at rest
-- **Compression** to reduce local and network overhead
-
-### Real-Time Sync and Offline-First
-RxDB's replication logic revolves around pulling down remote changes and pushing up local modifications. It maintains a checkpoint-based mechanism, so only new or updated documents flow between the client and server, reducing bandwidth usage and latency. This ensures:
-
-- **Live Data**: Queries automatically reflect server-side changes once they arrive locally.
-- **Background Updates**: No manual polling needed; replication streams or intervals handle synchronization.
-- **Conflict Handling** (see below) ensures data merges gracefully when multiple clients edit the same document offline.
-
-#### Multiple Replication Plugins and Approaches
-RxDB's flexible replication system lets you connect to different backends or even peer-to-peer networks. There are official plugins for [CouchDB](../replication-couchdb.md), [Firestore](../replication-firestore.md), [GraphQL](../replication-graphql.md), [WebRTC](../replication-webrtc.md), and more. Many developers create a **custom HTTP replication** to work with their existing REST-based backend, ensuring a painless integration that doesn't require adopting an entirely new server infrastructure.
-
-#### Example Setup of a local database
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-async function initZeroLocalDB() {
- // Create a local RxDB instance using localstorage-based storage
- const db = await createRxDatabase({
- name: 'myZeroLocalDB',
- storage: getRxStorageLocalstorage(),
- // optional: password for encryption if needed
- });
-
- // Define one or more collections
- await db.addCollections({
- tasks: {
- schema: {
- title: 'task schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string', maxLength: 100 },
- title: { type: 'string' },
- done: { type: 'boolean' }
- }
- }
- }
- });
-
- // Reactive query - automatically updates on local or remote changes
- db.tasks
- .find()
- .$ // returns an RxJS Observable
- .subscribe(allTasks => {
- console.log('All tasks updated:', allTasks);
- });
-
- return db;
-}
-```
-
-When offline, reads and writes to `db.tasks` happen locally with near-zero delay. Once connectivity resumes, changes sync to the server automatically (if replication is configured).
-
-#### Example Setup of the replication
-
-```ts
-import { replicateRxCollection } from 'rxdb/plugins/replication';
-
-async function syncLocalTasks(db) {
- replicateRxCollection({
- collection: db.tasks,
- replicationIdentifier: 'sync-tasks',
- // Define how to pull server documents and push local documents
- pull: {
- handler: async (lastCheckpoint, batchSize) => {
- // logic to retrieve updated tasks from the server since lastCheckpoint
- },
- },
- push: {
- handler: async (docs) => {
- // logic to post local changes to the server
- },
- },
- live: true, // continuously replicate
- retryTime: 5000, // retry on errors or disconnections
- });
-}
-```
-
-This replication seamlessly merges server-side and client-side changes. Your app remains responsive throughout, regardless of the network status.
-
-## Things you should also know about
-
-### Optimistic UI on Local Data Changes
-
-A local first approach, especially with RxDB, naturally supports an [optimistic UI](./optimistic-ui.md) pattern. Because writes occur on the client, you can instantly reflect changes in the interface as soon as the user performs an action - no need to wait for server confirmation. For example, when a user updates a task document to done: true, the UI can re-render immediately with that new state. This even works across multiple browser tabs.
-
-
-
-If a server conflict arises later during replication, RxDB's [conflict handling](../transactions-conflicts-revisions.md) logic determines which changes to keep, and the UI can be updated accordingly. This is far more efficient than blocking the user or displaying a spinner while the backend processes the request.
-
-### Conflict Handling
-
-In local first models, conflicts emerge if multiple devices or clients edit the same document while offline. RxDB tracks document revisions so you can detect collisions and merge them effectively. By default, RxDB uses a last-write-wins approach, but developers can override it with a custom conflict handler. This provides fine-grained control - like merging partial fields, storing revision histories, or prompting users for resolution. Proper conflict handling keeps distributed data consistent across your entire system.
-
-### Schema Migrations
-
-Over time, apps evolve - new fields, changed field types, or altered indexes. RxDB allows incremental schema migrations so you can upgrade a user's local data from one schema version to another. You might, for instance, rename a property or transform data formats. Once you define your migration strategy, RxDB automatically applies it upon app initialization, ensuring the local database's structure aligns with your latest codebase.
-
-## Advanced Features
-
-### Setup Encryption
-When storing data locally, you may handle user-sensitive information like PII (Personal Identifiable Information) or financial details. RxDB supports on-device [encryption](../encryption.md) to protect fields. For example, you can define:
-
-```ts
-import { wrappedKeyEncryptionCryptoJsStorage } from 'rxdb/plugins/encryption-crypto-js';
-
-const encryptedStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageLocalstorage()
-});
-
-const db = await createRxDatabase({
- name: 'secureDB',
- storage: encryptedStorage,
- password: 'myEncryptionPassword'
-});
-
-await db.addCollections({
- secrets: {
- schema: {
- title: 'secrets schema',
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string', maxLength: 100 },
- secretField: { type: 'string' }
- },
- required: ['id'],
- encrypted: ['secretField'] // define which fields to encrypt
- }
- }
-});
-```
-
-Then mark fields as `encrypted` in the schema. This ensures data is unreadable on disk without the correct password.
-
-### Setup Compression
-
-Local data can expand quickly, especially for large documents or repeated key names. RxDB's key compression feature replaces verbose field names with shorter tokens, decreasing storage usage and speeding up replication. You enable it by adding keyCompression: true to your collection schema:
-
-```ts
-await db.addCollections({
- logs: {
- schema: {
- title: 'log schema',
- version: 0,
- keyCompression: true,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string'. maxLength: 100 },
- message: { type: 'string' },
- timestamp: { type: 'number' }
- }
- }
- }
-});
-```
-
-## Different RxDB Storages Depending on the Runtime
-
-RxDB's storage layer is swappable, so you can pick the optimal adapter for each environment. Some common choices include:
-
-- [IndexedDB](../rx-storage-indexeddb.md) in modern browsers (default).
-- [OPFS](../rx-storage-opfs.md) (Origin Private File System) in browsers that support it for potentially better performance.
-- [SQLite](../rx-storage-sqlite.md) for mobile or desktop environments via the premium plugin, offering native-like speed on Android, iOS, or Electron.
-- [In-Memory](../rx-storage-memory.md) for tests or ephemeral data.
-
-By choosing a suitable storage layer, you can adapt your zero-latency local first design to any runtime - web, [mobile](./mobile-database.md), or server-like contexts in [Node.js](../nodejs-database.md).
-
-## Performance Considerations
-
-Performant local data operations are crucial for a zero-latency experience. According to the RxDB [storage performance overview](../rx-storage-performance.md), differences in underlying storages can significantly impact throughput and latency. For instance, IndexedDB typically performs well across modern browsers, [OPFS](../rx-storage-opfs.md) offers improved throughput in supporting browsers, and [SQLite storage](../rx-storage-sqlite.md) (a premium plugin) often delivers near-native speed for mobile or desktop.
-
-### Offloading Work from the Main Thread
-
-In a browser environment, you can move database operations into a Web Worker using the [Worker RxStorage plugin](../rx-storage-worker.md). This approach lets you keep heavy data processing off the main thread, ensuring the UI remains smooth and responsive. Complex queries or large write operations no longer cause stuttering in the user interface.
-
-### Sharding or Memory-Mapped Storages
-
-For large datasets or high concurrency, advanced techniques like [sharding](../rx-storage-sharding.md) collections across multiple storages or leveraging a [memory-mapped](../rx-storage-memory-mapped.md) variant can further boost performance. By splitting data into smaller subsets or streaming it only as needed, you can scale to handle complex usage scenarios without compromising on the zero-latency user experience.
-
-## Follow Up
-
-- Dive into the [RxDB Quickstart](../quickstart.md) to set up your own local first database.
-- Explore [Replication Plugins](../replication.md) for syncing with platforms like [CouchDB](../replication-couchdb.md), [Firestore](./firestore-alternative.md), or [GraphQL](../replication-graphql.md).
-- Check out Advanced [Conflict Handling](../transactions-conflicts-revisions.md) and [Performance Tuning](../rx-storage-performance.md) for big data sets or complex multi-user interactions.
-- Join the RxDB Community on [GitHub](/code/) and [Discord](/chat/) to share insights, file issues, and learn from other developers building zero-latency solutions.
--
-By integrating RxDB into your stack, you achieve millisecond interactions, full [offline capabilities](../offline-first.md), secure data at rest, and minimal overhead for large or distributed teams. This zero-latency local first architecture is the future of modern software - delivering a fluid, always-available user experience without overcomplicating the developer workflow.
diff --git a/docs/assets/css/styles.b066e612.css b/docs/assets/css/styles.b066e612.css
deleted file mode 100644
index 327f2929ef3..00000000000
--- a/docs/assets/css/styles.b066e612.css
+++ /dev/null
@@ -1 +0,0 @@
-.col,.container{padding:0 var(--ifm-spacing-horizontal);width:100%}.col,.container,html{width:100%}.markdown>h2,.markdown>h3,.markdown>h4,.markdown>h5,.markdown>h6{margin-bottom:calc(var(--ifm-heading-vertical-rhythm-bottom)*var(--ifm-leading))}html,ol ol,ol ul,ul ol,ul ul{margin:0}pre,table{overflow:auto}blockquote,pre{margin:0 0 var(--ifm-spacing-vertical)}.breadcrumbs__link,.button{transition-timing-function:var(--ifm-transition-timing-default)}.button--outline.button--active,.button--outline:active,.button--outline:hover,:root{--ifm-button-color:var(--ifm-font-color-base-inverse)}.menu__link:hover,a{transition:color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.navbar--dark,:root{--ifm-navbar-link-hover-color:var(--ifm-color-primary)}.menu,.navbar-sidebar,html{overflow-x:hidden}:root,html[data-theme=dark]{--ifm-color-emphasis-500:var(--ifm-color-gray-500)}.block table,.premium-request iframe{box-shadow:0 0 12px 8px var(--bg-color)}#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll,.slick-dots li.slick-active button:before{background-color:var(--color-top)!important}.navbar,.side-tabs__tab,.slick-slide{align-self:stretch}.toggleButton_gllP,html{-webkit-tap-highlight-color:transparent}.searchbox__reset:focus,.searchbox__submit:focus,body:not(.navigation-with-keyboard) :not(input):focus{outline:0}.markdown li,body{word-wrap:break-word}.clean-list,.containsTaskList_mC6p,.details_lb9f>summary,.dropdown__menu,.menu__list{list-style:none}:root{--ifm-color-scheme:light;--ifm-dark-value:10%;--ifm-darker-value:15%;--ifm-darkest-value:30%;--ifm-light-value:15%;--ifm-lighter-value:30%;--ifm-lightest-value:50%;--ifm-contrast-background-value:90%;--ifm-contrast-foreground-value:70%;--ifm-contrast-background-dark-value:70%;--ifm-contrast-foreground-dark-value:90%;--ifm-color-primary:#3578e5;--ifm-color-secondary:#ebedf0;--ifm-color-success:#00a400;--ifm-color-info:#54c7ec;--ifm-color-warning:#ffba00;--ifm-color-danger:#fa383e;--ifm-color-primary-dark:#306cce;--ifm-color-primary-darker:#2d66c3;--ifm-color-primary-darkest:#2554a0;--ifm-color-primary-light:#538ce9;--ifm-color-primary-lighter:#72a1ed;--ifm-color-primary-lightest:#9abcf2;--ifm-color-primary-contrast-background:#ebf2fc;--ifm-color-primary-contrast-foreground:#102445;--ifm-color-secondary-dark:#d4d5d8;--ifm-color-secondary-darker:#c8c9cc;--ifm-color-secondary-darkest:#a4a6a8;--ifm-color-secondary-light:#eef0f2;--ifm-color-secondary-lighter:#f1f2f5;--ifm-color-secondary-lightest:#f5f6f8;--ifm-color-secondary-contrast-background:#fdfdfe;--ifm-color-secondary-contrast-foreground:#474748;--ifm-color-success-dark:#009400;--ifm-color-success-darker:#008b00;--ifm-color-success-darkest:#007300;--ifm-color-success-light:#26b226;--ifm-color-success-lighter:#4dbf4d;--ifm-color-success-lightest:#80d280;--ifm-color-success-contrast-background:#e6f6e6;--ifm-color-success-contrast-foreground:#003100;--ifm-color-info-dark:#4cb3d4;--ifm-color-info-darker:#47a9c9;--ifm-color-info-darkest:#3b8ba5;--ifm-color-info-light:#6ecfef;--ifm-color-info-lighter:#87d8f2;--ifm-color-info-lightest:#aae3f6;--ifm-color-info-contrast-background:#eef9fd;--ifm-color-info-contrast-foreground:#193c47;--ifm-color-warning-dark:#e6a700;--ifm-color-warning-darker:#d99e00;--ifm-color-warning-darkest:#b38200;--ifm-color-warning-light:#ffc426;--ifm-color-warning-lighter:#ffcf4d;--ifm-color-warning-lightest:#ffdd80;--ifm-color-warning-contrast-background:#fff8e6;--ifm-color-warning-contrast-foreground:#4d3800;--ifm-color-danger-dark:#e13238;--ifm-color-danger-darker:#d53035;--ifm-color-danger-darkest:#af272b;--ifm-color-danger-light:#fb565b;--ifm-color-danger-lighter:#fb7478;--ifm-color-danger-lightest:#fd9c9f;--ifm-color-danger-contrast-background:#ffebec;--ifm-color-danger-contrast-foreground:#4b1113;--ifm-color-white:#fff;--ifm-color-black:#000;--ifm-color-gray-0:var(--ifm-color-white);--ifm-color-gray-100:#f5f6f7;--ifm-color-gray-200:#ebedf0;--ifm-color-gray-300:#dadde1;--ifm-color-gray-400:#ccd0d5;--ifm-color-gray-500:#bec3c9;--ifm-color-gray-600:#8d949e;--ifm-color-gray-700:#606770;--ifm-color-gray-800:#444950;--ifm-color-gray-900:#1c1e21;--ifm-color-gray-1000:var(--ifm-color-black);--ifm-color-emphasis-0:var(--ifm-color-gray-0);--ifm-color-emphasis-100:var(--ifm-color-gray-100);--ifm-color-emphasis-200:var(--ifm-color-gray-200);--ifm-color-emphasis-300:var(--ifm-color-gray-300);--ifm-color-emphasis-400:var(--ifm-color-gray-400);--ifm-color-emphasis-600:var(--ifm-color-gray-600);--ifm-color-emphasis-700:var(--ifm-color-gray-700);--ifm-color-emphasis-800:var(--ifm-color-gray-800);--ifm-color-emphasis-900:var(--ifm-color-gray-900);--ifm-color-emphasis-1000:var(--ifm-color-gray-1000);--ifm-color-content:var(--ifm-color-emphasis-900);--ifm-color-content-inverse:var(--ifm-color-emphasis-0);--ifm-color-content-secondary:#525860;--ifm-background-color:#0000;--ifm-background-surface-color:var(--ifm-color-content-inverse);--ifm-global-border-width:1px;--ifm-global-radius:0.4rem;--ifm-hover-overlay:#0000000d;--ifm-font-color-base:var(--ifm-color-content);--ifm-font-color-base-inverse:var(--ifm-color-content-inverse);--ifm-font-color-secondary:var(--ifm-color-content-secondary);--ifm-font-family-base:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";--ifm-font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--ifm-font-size-base:100%;--ifm-font-weight-light:300;--ifm-font-weight-normal:400;--ifm-font-weight-semibold:500;--ifm-font-weight-bold:700;--ifm-font-weight-base:var(--ifm-font-weight-normal);--ifm-line-height-base:1.65;--ifm-global-spacing:1rem;--ifm-spacing-vertical:var(--ifm-global-spacing);--ifm-spacing-horizontal:var(--ifm-global-spacing);--ifm-transition-fast:200ms;--ifm-transition-slow:400ms;--ifm-transition-timing-default:cubic-bezier(0.08,0.52,0.52,1);--ifm-global-shadow-lw:0 1px 2px 0 #0000001a;--ifm-global-shadow-md:0 5px 40px #0003;--ifm-global-shadow-tl:0 12px 28px 0 #0003,0 2px 4px 0 #0000001a;--ifm-z-index-dropdown:100;--ifm-z-index-fixed:200;--ifm-z-index-overlay:400;--ifm-container-width:1140px;--ifm-container-width-xl:1320px;--ifm-code-background:#f6f7f8;--ifm-code-border-radius:var(--ifm-global-radius);--ifm-code-font-size:90%;--ifm-code-padding-horizontal:0.1rem;--ifm-code-padding-vertical:0.1rem;--ifm-pre-background:var(--ifm-code-background);--ifm-pre-border-radius:var(--ifm-code-border-radius);--ifm-pre-color:inherit;--ifm-pre-line-height:1.45;--ifm-pre-padding:1rem;--ifm-heading-color:inherit;--ifm-heading-margin-top:0;--ifm-heading-margin-bottom:var(--ifm-spacing-vertical);--ifm-heading-font-family:var(--ifm-font-family-base);--ifm-heading-font-weight:var(--ifm-font-weight-bold);--ifm-heading-line-height:1.25;--ifm-h1-font-size:2rem;--ifm-h2-font-size:1.5rem;--ifm-h3-font-size:1.25rem;--ifm-h4-font-size:1rem;--ifm-h5-font-size:0.875rem;--ifm-h6-font-size:0.85rem;--ifm-image-alignment-padding:1.25rem;--ifm-leading-desktop:1.25;--ifm-leading:calc(var(--ifm-leading-desktop)*1rem);--ifm-list-left-padding:2rem;--ifm-list-margin:1rem;--ifm-list-item-margin:0.25rem;--ifm-list-paragraph-margin:1rem;--ifm-table-cell-padding:0.75rem;--ifm-table-background:#0000;--ifm-table-stripe-background:#00000008;--ifm-table-border-width:1px;--ifm-table-border-color:var(--ifm-color-emphasis-300);--ifm-table-head-background:inherit;--ifm-table-head-color:inherit;--ifm-table-head-font-weight:var(--ifm-font-weight-bold);--ifm-table-cell-color:inherit;--ifm-link-color:var(--ifm-color-primary);--ifm-link-decoration:none;--ifm-link-hover-color:var(--ifm-link-color);--ifm-link-hover-decoration:underline;--ifm-paragraph-margin-bottom:var(--ifm-leading);--ifm-blockquote-font-size:var(--ifm-font-size-base);--ifm-blockquote-border-left-width:2px;--ifm-blockquote-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-blockquote-padding-vertical:0;--ifm-blockquote-shadow:none;--ifm-blockquote-color:var(--ifm-color-emphasis-800);--ifm-blockquote-border-color:var(--ifm-color-emphasis-300);--ifm-hr-background-color:var(--ifm-color-emphasis-500);--ifm-hr-height:1px;--ifm-hr-margin-vertical:1.5rem;--ifm-scrollbar-size:7px;--ifm-scrollbar-track-background-color:#f1f1f1;--ifm-scrollbar-thumb-background-color:silver;--ifm-scrollbar-thumb-hover-background-color:#a7a7a7;--ifm-alert-background-color:inherit;--ifm-alert-border-color:inherit;--ifm-alert-border-radius:var(--ifm-global-radius);--ifm-alert-border-width:0px;--ifm-alert-border-left-width:5px;--ifm-alert-color:var(--ifm-font-color-base);--ifm-alert-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-alert-padding-vertical:var(--ifm-spacing-vertical);--ifm-alert-shadow:var(--ifm-global-shadow-lw);--ifm-avatar-intro-margin:1rem;--ifm-avatar-intro-alignment:inherit;--ifm-avatar-photo-size:3rem;--ifm-badge-background-color:inherit;--ifm-badge-border-color:inherit;--ifm-badge-border-radius:var(--ifm-global-radius);--ifm-badge-border-width:var(--ifm-global-border-width);--ifm-badge-color:var(--ifm-color-white);--ifm-badge-padding-horizontal:calc(var(--ifm-spacing-horizontal)*0.5);--ifm-badge-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-breadcrumb-border-radius:1.5rem;--ifm-breadcrumb-spacing:0.5rem;--ifm-breadcrumb-color-active:var(--ifm-color-primary);--ifm-breadcrumb-item-background-active:var(--ifm-hover-overlay);--ifm-breadcrumb-padding-horizontal:0.8rem;--ifm-breadcrumb-padding-vertical:0.4rem;--ifm-breadcrumb-size-multiplier:1;--ifm-breadcrumb-separator:url('data:image/svg+xml;utf8,');--ifm-breadcrumb-separator-filter:none;--ifm-breadcrumb-separator-size:0.5rem;--ifm-breadcrumb-separator-size-multiplier:1.25;--ifm-button-background-color:inherit;--ifm-button-border-color:var(--ifm-button-background-color);--ifm-button-border-width:var(--ifm-global-border-width);--ifm-button-font-weight:var(--ifm-font-weight-bold);--ifm-button-padding-horizontal:1.5rem;--ifm-button-padding-vertical:0.375rem;--ifm-button-size-multiplier:1;--ifm-button-transition-duration:var(--ifm-transition-fast);--ifm-button-border-radius:calc(var(--ifm-global-radius)*var(--ifm-button-size-multiplier));--ifm-button-group-spacing:2px;--ifm-card-background-color:var(--ifm-background-surface-color);--ifm-card-border-radius:calc(var(--ifm-global-radius)*2);--ifm-card-horizontal-spacing:var(--ifm-global-spacing);--ifm-card-vertical-spacing:var(--ifm-global-spacing);--ifm-toc-border-color:var(--ifm-color-emphasis-300);--ifm-toc-link-color:var(--ifm-color-content-secondary);--ifm-toc-padding-vertical:0.5rem;--ifm-toc-padding-horizontal:0.5rem;--ifm-dropdown-background-color:var(--ifm-background-surface-color);--ifm-dropdown-font-weight:var(--ifm-font-weight-semibold);--ifm-dropdown-link-color:var(--ifm-font-color-base);--ifm-dropdown-hover-background-color:var(--ifm-hover-overlay);--ifm-footer-background-color:var(--ifm-color-emphasis-100);--ifm-footer-color:inherit;--ifm-footer-link-color:var(--ifm-color-emphasis-700);--ifm-footer-link-hover-color:var(--ifm-color-primary);--ifm-footer-link-horizontal-spacing:0.5rem;--ifm-footer-padding-horizontal:calc(var(--ifm-spacing-horizontal)*2);--ifm-footer-padding-vertical:calc(var(--ifm-spacing-vertical)*2);--ifm-footer-title-color:inherit;--ifm-footer-logo-max-width:min(30rem,90vw);--ifm-hero-background-color:var(--ifm-background-surface-color);--ifm-hero-text-color:var(--ifm-color-emphasis-800);--ifm-menu-color:var(--ifm-color-emphasis-700);--ifm-menu-color-active:var(--ifm-color-primary);--ifm-menu-color-background-active:var(--ifm-hover-overlay);--ifm-menu-color-background-hover:var(--ifm-hover-overlay);--ifm-menu-link-padding-horizontal:0.75rem;--ifm-menu-link-padding-vertical:0.375rem;--ifm-menu-link-sublist-icon:url('data:image/svg+xml;utf8,');--ifm-menu-link-sublist-icon-filter:none;--ifm-navbar-background-color:var(--ifm-background-surface-color);--ifm-navbar-height:3.75rem;--ifm-navbar-item-padding-horizontal:0.75rem;--ifm-navbar-item-padding-vertical:0.25rem;--ifm-navbar-link-color:var(--ifm-font-color-base);--ifm-navbar-link-active-color:var(--ifm-link-color);--ifm-navbar-padding-horizontal:var(--ifm-spacing-horizontal);--ifm-navbar-padding-vertical:calc(var(--ifm-spacing-vertical)*0.5);--ifm-navbar-shadow:var(--ifm-global-shadow-lw);--ifm-navbar-search-input-background-color:var(--ifm-color-emphasis-200);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-800);--ifm-navbar-search-input-placeholder-color:var(--ifm-color-emphasis-500);--ifm-navbar-search-input-icon:url('data:image/svg+xml;utf8,');--ifm-navbar-sidebar-width:83vw;--ifm-pagination-border-radius:var(--ifm-global-radius);--ifm-pagination-color-active:var(--ifm-color-primary);--ifm-pagination-font-size:1rem;--ifm-pagination-item-active-background:var(--ifm-hover-overlay);--ifm-pagination-page-spacing:0.2em;--ifm-pagination-padding-horizontal:calc(var(--ifm-spacing-horizontal)*1);--ifm-pagination-padding-vertical:calc(var(--ifm-spacing-vertical)*0.25);--ifm-pagination-nav-border-radius:var(--ifm-global-radius);--ifm-pagination-nav-color-hover:var(--ifm-color-primary);--ifm-pills-color-active:var(--ifm-color-primary);--ifm-pills-color-background-active:var(--ifm-hover-overlay);--ifm-pills-spacing:0.125rem;--ifm-tabs-color:var(--ifm-font-color-secondary);--ifm-tabs-color-active:var(--ifm-color-primary);--ifm-tabs-color-active-border:var(--ifm-tabs-color-active);--ifm-tabs-padding-horizontal:1rem;--ifm-tabs-padding-vertical:1rem}:root,[data-theme=dark]{--ifm-color-primary:#ed168f;--ifm-color-primary-dark:#ed168f;--ifm-color-primary-darker:#8d2089;--ifm-color-primary-darkest:#5f2688;--ifm-color-primary-light:#29d5b0;--ifm-color-primary-lighter:#32d8b4;--ifm-color-primary-lightest:#4fddbf}.badge--danger,.badge--info,.badge--primary,.badge--secondary,.badge--success,.badge--warning{--ifm-badge-border-color:var(--ifm-badge-background-color)}.button--link,.button--outline{--ifm-button-background-color:#0000}*,.algolia-autocomplete .ds-dropdown-menu *{box-sizing:border-box}html{background-color:var(--ifm-background-color);color:var(--ifm-font-color-base);color-scheme:var(--ifm-color-scheme);font:var(--ifm-font-size-base)/var(--ifm-line-height-base) var(--ifm-font-family-base);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;-webkit-text-size-adjust:100%;text-size-adjust:100%;min-height:100%;padding:0}iframe{border:0;color-scheme:auto}.container{margin:0 auto;max-width:var(--ifm-container-width)}.container--fluid{max-width:inherit}.row{display:flex;flex-wrap:wrap;margin:0 calc(var(--ifm-spacing-horizontal)*-1)}.margin-bottom--none,.margin-vert--none,.markdown>:last-child{margin-bottom:0!important}.margin-top--none,.margin-vert--none{margin-top:0!important}.row--no-gutters{margin-left:0;margin-right:0}.margin-horiz--none,.margin-right--none{margin-right:0!important}.row--no-gutters>.col{padding-left:0;padding-right:0}.row--align-top{align-items:flex-start}.row--align-bottom{align-items:flex-end}.menuExternalLink_NmtK,.row--align-center{align-items:center}.row--align-stretch{align-items:stretch}.row--align-baseline{align-items:baseline}.col{--ifm-col-width:100%;flex:1 0;margin-left:0;max-width:var(--ifm-col-width)}.padding-bottom--none,.padding-vert--none{padding-bottom:0!important}.padding-top--none,.padding-vert--none{padding-top:0!important}.padding-horiz--none,.padding-left--none{padding-left:0!important}.padding-horiz--none,.padding-right--none{padding-right:0!important}.col[class*=col--]{flex:0 0 var(--ifm-col-width)}.col--1{--ifm-col-width:8.33333%}.col--offset-1{margin-left:8.33333%}.col--2{--ifm-col-width:16.66667%}.col--offset-2{margin-left:16.66667%}.col--3{--ifm-col-width:25%}.col--offset-3{margin-left:25%}.col--4{--ifm-col-width:33.33333%}.col--offset-4{margin-left:33.33333%}.col--5{--ifm-col-width:41.66667%}.col--offset-5{margin-left:41.66667%}.col--6{--ifm-col-width:50%}.col--offset-6{margin-left:50%}.col--7{--ifm-col-width:58.33333%}.col--offset-7{margin-left:58.33333%}.col--8{--ifm-col-width:66.66667%}.col--offset-8{margin-left:66.66667%}.col--9{--ifm-col-width:75%}.col--offset-9{margin-left:75%}.col--10{--ifm-col-width:83.33333%}.col--offset-10{margin-left:83.33333%}.col--11{--ifm-col-width:91.66667%}.col--offset-11{margin-left:91.66667%}.col--12{--ifm-col-width:100%}.col--offset-12{margin-left:100%}.margin-horiz--none,.margin-left--none{margin-left:0!important}.margin--none{margin:0!important}.margin-bottom--xs,.margin-vert--xs{margin-bottom:.25rem!important}.margin-top--xs,.margin-vert--xs{margin-top:.25rem!important}.margin-horiz--xs,.margin-left--xs{margin-left:.25rem!important}.margin-horiz--xs,.margin-right--xs{margin-right:.25rem!important}.margin--xs{margin:.25rem!important}.margin-bottom--sm,.margin-vert--sm{margin-bottom:.5rem!important}.margin-top--sm,.margin-vert--sm{margin-top:.5rem!important}.margin-horiz--sm,.margin-left--sm{margin-left:.5rem!important}.margin-horiz--sm,.margin-right--sm{margin-right:.5rem!important}.margin--sm{margin:.5rem!important}.margin-bottom--md,.margin-vert--md{margin-bottom:1rem!important}.margin-top--md,.margin-vert--md{margin-top:1rem!important}.margin-horiz--md,.margin-left--md{margin-left:1rem!important}.margin-horiz--md,.margin-right--md{margin-right:1rem!important}.margin--md{margin:1rem!important}.margin-bottom--lg,.margin-vert--lg{margin-bottom:2rem!important}.margin-top--lg,.margin-vert--lg{margin-top:2rem!important}.margin-horiz--lg,.margin-left--lg{margin-left:2rem!important}.margin-horiz--lg,.margin-right--lg{margin-right:2rem!important}.margin--lg{margin:2rem!important}.margin-bottom--xl,.margin-vert--xl{margin-bottom:5rem!important}.margin-top--xl,.margin-vert--xl{margin-top:5rem!important}.margin-horiz--xl,.margin-left--xl{margin-left:5rem!important}.margin-horiz--xl,.margin-right--xl{margin-right:5rem!important}.margin--xl{margin:5rem!important}.padding--none{padding:0!important}.padding-bottom--xs,.padding-vert--xs{padding-bottom:.25rem!important}.padding-top--xs,.padding-vert--xs{padding-top:.25rem!important}.padding-horiz--xs,.padding-left--xs{padding-left:.25rem!important}.padding-horiz--xs,.padding-right--xs{padding-right:.25rem!important}.padding--xs{padding:.25rem!important}.padding-bottom--sm,.padding-vert--sm{padding-bottom:.5rem!important}.padding-top--sm,.padding-vert--sm{padding-top:.5rem!important}.padding-horiz--sm,.padding-left--sm{padding-left:.5rem!important}.padding-horiz--sm,.padding-right--sm{padding-right:.5rem!important}.padding--sm{padding:.5rem!important}.padding-bottom--md,.padding-vert--md{padding-bottom:1rem!important}.padding-top--md,.padding-vert--md{padding-top:1rem!important}.padding-horiz--md,.padding-left--md{padding-left:1rem!important}.padding-horiz--md,.padding-right--md{padding-right:1rem!important}.padding--md{padding:1rem!important}.padding-bottom--lg,.padding-vert--lg{padding-bottom:2rem!important}.padding-top--lg,.padding-vert--lg{padding-top:2rem!important}.padding-horiz--lg,.padding-left--lg{padding-left:2rem!important}.padding-horiz--lg,.padding-right--lg{padding-right:2rem!important}.padding--lg{padding:2rem!important}.padding-bottom--xl,.padding-vert--xl{padding-bottom:5rem!important}.padding-top--xl,.padding-vert--xl{padding-top:5rem!important}.padding-horiz--xl,.padding-left--xl{padding-left:5rem!important}.padding-horiz--xl,.padding-right--xl{padding-right:5rem!important}.padding--xl{padding:5rem!important}code{background-color:var(--ifm-code-background);border:.1rem solid #0000001a;border-radius:var(--ifm-code-border-radius);font-family:var(--ifm-font-family-monospace);font-size:var(--ifm-code-font-size);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal);vertical-align:middle}a code{color:inherit}pre{background-color:var(--ifm-pre-background);border-radius:var(--ifm-pre-border-radius);color:var(--ifm-pre-color);font:var(--ifm-code-font-size)/var(--ifm-pre-line-height) var(--ifm-font-family-monospace);padding:var(--ifm-pre-padding)}pre code{background-color:initial;border:none;font-size:100%;line-height:inherit;padding:0;background-color:var(--bg-color-code)}kbd{background-color:var(--ifm-color-emphasis-0);border:1px solid var(--ifm-color-emphasis-400);border-radius:.2rem;box-shadow:inset 0 -1px 0 var(--ifm-color-emphasis-400);color:var(--ifm-color-emphasis-800);font:80% var(--ifm-font-family-monospace);padding:.15rem .3rem}h1,h2,h3,h4,h5,h6{color:var(--ifm-heading-color);font-family:var(--ifm-heading-font-family);font-weight:var(--ifm-heading-font-weight);line-height:var(--ifm-heading-line-height);margin:var(--ifm-heading-margin-top) 0 var(--ifm-heading-margin-bottom) 0}h1{font-size:var(--ifm-h1-font-size)}h2{font-size:var(--ifm-h2-font-size);margin-top:0}h3{font-size:var(--ifm-h3-font-size)}h4{font-size:var(--ifm-h4-font-size)}h5{font-size:var(--ifm-h5-font-size)}h6{font-size:var(--ifm-h6-font-size)}img{max-width:100%}img[align=right]{padding-left:var(--image-alignment-padding)}img[align=left]{padding-right:var(--image-alignment-padding)}.markdown{--ifm-h1-vertical-rhythm-top:3;--ifm-h2-vertical-rhythm-top:2;--ifm-h3-vertical-rhythm-top:1.5;--ifm-heading-vertical-rhythm-top:1.25;--ifm-h1-vertical-rhythm-bottom:1.25;--ifm-heading-vertical-rhythm-bottom:1}.markdown:after,.markdown:before{content:"";display:table}.clear,.markdown:after{clear:both}.markdown h1:first-child{--ifm-h1-font-size:3rem;margin-bottom:calc(var(--ifm-h1-vertical-rhythm-bottom)*var(--ifm-leading))}.markdown>h2{--ifm-h2-font-size:2rem;margin-top:calc(var(--ifm-h2-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h3{--ifm-h3-font-size:1.5rem;margin-top:calc(var(--ifm-h3-vertical-rhythm-top)*var(--ifm-leading))}.markdown>h4,.markdown>h5,.markdown>h6{margin-top:calc(var(--ifm-heading-vertical-rhythm-top)*var(--ifm-leading))}.markdown>p,.markdown>pre,.markdown>ul{margin-bottom:var(--ifm-leading)}.markdown li>p{margin-top:var(--ifm-list-paragraph-margin)}.markdown li+li{margin-top:var(--ifm-list-item-margin)}ol,ul{margin:0 0 var(--ifm-list-margin);padding-left:var(--ifm-list-left-padding)}ol ol,ul ol{list-style-type:lower-roman}ol ol ol,ol ul ol,ul ol ol,ul ul ol{list-style-type:lower-alpha}table{border-collapse:collapse;display:block;margin-bottom:var(--ifm-spacing-vertical)}table thead tr{border-bottom:2px solid var(--ifm-table-border-color)}table thead,table tr:nth-child(2n){background-color:var(--ifm-table-stripe-background)}table tr{background-color:var(--ifm-table-background);border-top:var(--ifm-table-border-width) solid var(--ifm-table-border-color)}table td,table th{border:var(--ifm-table-border-width) solid var(--ifm-table-border-color);padding:var(--ifm-table-cell-padding)}table th{background-color:var(--ifm-table-head-background);color:var(--ifm-table-head-color);font-weight:var(--ifm-table-head-font-weight)}table td{color:var(--ifm-table-cell-color)}strong{font-weight:var(--ifm-font-weight-bold)}a{color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}a:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.button:hover,.text--no-decoration,.text--no-decoration:hover,a:not([href]){-webkit-text-decoration:none;text-decoration:none}blockquote{border-left:var(--ifm-blockquote-border-left-width) solid var(--ifm-blockquote-border-color);box-shadow:var(--ifm-blockquote-shadow);color:var(--ifm-blockquote-color);font-size:var(--ifm-blockquote-font-size);padding:var(--ifm-blockquote-padding-vertical) var(--ifm-blockquote-padding-horizontal)}blockquote>:first-child{margin-top:0}blockquote>:last-child{margin-bottom:0}hr{background-color:var(--ifm-hr-background-color);border:0;height:var(--ifm-hr-height);margin:var(--ifm-hr-margin-vertical) 0}.shadow--lw{box-shadow:var(--ifm-global-shadow-lw)!important}.shadow--md{box-shadow:var(--ifm-global-shadow-md)!important}.shadow--tl{box-shadow:var(--ifm-global-shadow-tl)!important}.text--primary,.wordWrapButtonEnabled_uzNF .wordWrapButtonIcon_b1P5{color:var(--ifm-color-primary)}.text--secondary{color:var(--ifm-color-secondary)}.text--success{color:var(--ifm-color-success)}.text--info{color:var(--ifm-color-info)}.text--warning{color:var(--ifm-color-warning)}.text--danger{color:var(--ifm-color-danger)}.block.fifth h2,.block.last h2,.block.offline-first h2,.block.replication h2,.block.sixth h2,.redirectBox .ul-container,.reviews h2,.text--center{text-align:center}.text--left{text-align:left}.text--justify{text-align:justify}#price-calculator-result table th,.text--right{text-align:right}.text--capitalize{text-transform:capitalize}.text--lowercase{text-transform:lowercase}.admonitionHeading_Gvgb,.alert__heading,.text--uppercase,.trophy .subtitle,.trophy .valuetitle{text-transform:uppercase}.text--light{font-weight:var(--ifm-font-weight-light)}.text--normal{font-weight:var(--ifm-font-weight-normal)}.text--semibold{font-weight:var(--ifm-font-weight-semibold)}.text--bold{font-weight:var(--ifm-font-weight-bold)}.text--italic,pre[data-theme] span[style*="--shiki-token-keyword"]{font-style:italic}.text--truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text--break{word-wrap:break-word!important;word-break:break-word!important}.clean-btn{background:none;border:none;color:inherit;cursor:pointer;font-family:inherit;padding:0}.alert,.alert .close{color:var(--ifm-alert-foreground-color)}.clean-list{padding-left:0}.alert--primary{--ifm-alert-background-color:var(--ifm-color-primary-contrast-background);--ifm-alert-background-color-highlight:#3578e526;--ifm-alert-foreground-color:var(--ifm-color-primary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-primary-dark)}.alert--secondary{--ifm-alert-background-color:var(--ifm-color-secondary-contrast-background);--ifm-alert-background-color-highlight:#ebedf026;--ifm-alert-foreground-color:var(--ifm-color-secondary-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-secondary-dark)}.alert--success{--ifm-alert-background-color:var(--ifm-color-success-contrast-background);--ifm-alert-background-color-highlight:#00a40026;--ifm-alert-foreground-color:var(--ifm-color-success-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-success-dark)}.alert--info{--ifm-alert-background-color:var(--ifm-color-info-contrast-background);--ifm-alert-background-color-highlight:#54c7ec26;--ifm-alert-foreground-color:var(--ifm-color-info-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-info-dark)}.alert--warning{--ifm-alert-background-color:var(--ifm-color-warning-contrast-background);--ifm-alert-background-color-highlight:#ffba0026;--ifm-alert-foreground-color:var(--ifm-color-warning-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-warning-dark)}.alert--danger{--ifm-alert-background-color:var(--ifm-color-danger-contrast-background);--ifm-alert-background-color-highlight:#fa383e26;--ifm-alert-foreground-color:var(--ifm-color-danger-contrast-foreground);--ifm-alert-border-color:var(--ifm-color-danger-dark)}.alert{--ifm-code-background:var(--ifm-alert-background-color-highlight);--ifm-link-color:var(--ifm-alert-foreground-color);--ifm-link-hover-color:var(--ifm-alert-foreground-color);--ifm-link-decoration:underline;--ifm-tabs-color:var(--ifm-alert-foreground-color);--ifm-tabs-color-active:var(--ifm-alert-foreground-color);--ifm-tabs-color-active-border:var(--ifm-alert-border-color);background-color:var(--ifm-alert-background-color);border:var(--ifm-alert-border-width) solid var(--ifm-alert-border-color);border-left-width:var(--ifm-alert-border-left-width);border-radius:var(--ifm-alert-border-radius);box-shadow:var(--ifm-alert-shadow);padding:var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal)}.alert__heading{align-items:center;display:flex;font:700 var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family);margin-bottom:.5rem}.alert__icon{display:inline-flex;margin-right:.4em}.alert__icon svg{fill:var(--ifm-alert-foreground-color);stroke:var(--ifm-alert-foreground-color);stroke-width:0}.alert .close{margin:calc(var(--ifm-alert-padding-vertical)*-1) calc(var(--ifm-alert-padding-horizontal)*-1) 0 0;opacity:.75}.alert .close:focus,.alert .close:hover,.hash-link:focus,:hover>.hash-link{opacity:1}.alert a{text-decoration-color:var(--ifm-alert-border-color)}.alert a:hover{text-decoration-thickness:2px}.avatar{column-gap:var(--ifm-avatar-intro-margin);display:flex}.avatar__photo{border-radius:50%;display:block;height:var(--ifm-avatar-photo-size);overflow:hidden;width:var(--ifm-avatar-photo-size)}.avatar__photo--sm{--ifm-avatar-photo-size:2rem}.avatar__photo--lg{--ifm-avatar-photo-size:4rem}.avatar__photo--xl{--ifm-avatar-photo-size:6rem}.avatar__intro{display:flex;flex:1 1;flex-direction:column;justify-content:center;text-align:var(--ifm-avatar-intro-alignment)}.badge,.breadcrumbs__item,.breadcrumbs__link,.button,.dropdown>.navbar__link:after{display:inline-block}.avatar__name{font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base)}.badge,.close{line-height:1}.avatar__subtitle{margin-top:.25rem}.avatar--vertical{--ifm-avatar-intro-alignment:center;--ifm-avatar-intro-margin:0.5rem;align-items:center;flex-direction:column}.badge{background-color:var(--ifm-badge-background-color);border:var(--ifm-badge-border-width) solid var(--ifm-badge-border-color);border-radius:var(--ifm-badge-border-radius);color:var(--ifm-badge-color);font-size:75%;font-weight:var(--ifm-font-weight-bold);padding:var(--ifm-badge-padding-vertical) var(--ifm-badge-padding-horizontal)}.badge--primary{--ifm-badge-background-color:var(--ifm-color-primary)}.badge--secondary{--ifm-badge-background-color:var(--ifm-color-secondary);color:var(--ifm-color-black)}.breadcrumbs__link,.button.button--secondary.button--outline:not(.button--active):not(:hover){color:var(--ifm-font-color-base)}.badge--success{--ifm-badge-background-color:var(--ifm-color-success)}.badge--info{--ifm-badge-background-color:var(--ifm-color-info)}.badge--warning{--ifm-badge-background-color:var(--ifm-color-warning)}.badge--danger{--ifm-badge-background-color:var(--ifm-color-danger)}.breadcrumbs{margin-bottom:0;padding-left:0}.breadcrumbs__item:not(:last-child):after{background:var(--ifm-breadcrumb-separator) center;content:" ";display:inline-block;filter:var(--ifm-breadcrumb-separator-filter);height:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier));margin:0 var(--ifm-breadcrumb-spacing);opacity:.5;width:calc(var(--ifm-breadcrumb-separator-size)*var(--ifm-breadcrumb-size-multiplier)*var(--ifm-breadcrumb-separator-size-multiplier))}.breadcrumbs__item--active .breadcrumbs__link{background:var(--ifm-breadcrumb-item-background-active);color:var(--ifm-breadcrumb-color-active)}.breadcrumbs__link{border-radius:var(--ifm-breadcrumb-border-radius);font-size:calc(1rem*var(--ifm-breadcrumb-size-multiplier));padding:calc(var(--ifm-breadcrumb-padding-vertical)*var(--ifm-breadcrumb-size-multiplier)) calc(var(--ifm-breadcrumb-padding-horizontal)*var(--ifm-breadcrumb-size-multiplier));transition-duration:var(--ifm-transition-fast);transition-property:background,color}.breadcrumbs__link:any-link:hover,.breadcrumbs__link:link:hover,.breadcrumbs__link:visited:hover,area[href].breadcrumbs__link:hover{background:var(--ifm-breadcrumb-item-background-active);-webkit-text-decoration:none;text-decoration:none}.breadcrumbs--sm{--ifm-breadcrumb-size-multiplier:0.8}.breadcrumbs--lg{--ifm-breadcrumb-size-multiplier:1.2}.button{background-color:var(--ifm-button-background-color);border:var(--ifm-button-border-width) solid var(--ifm-button-border-color);border-radius:var(--ifm-button-border-radius);font-size:calc(.875rem*var(--ifm-button-size-multiplier));font-weight:var(--ifm-button-font-weight);padding:calc(var(--ifm-button-padding-vertical)*var(--ifm-button-size-multiplier)) calc(var(--ifm-button-padding-horizontal)*var(--ifm-button-size-multiplier));transition-duration:var(--ifm-button-transition-duration);white-space:nowrap}.button,.button:hover{color:var(--ifm-button-color)}.button:hover{box-shadow:0 6px px #ca007c,-2px -1px 14px #ff009e}.button--outline{--ifm-button-color:var(--ifm-button-border-color)}.button--outline:hover{--ifm-button-background-color:var(--ifm-button-border-color)}.button--link{--ifm-button-border-color:#0000;color:var(--ifm-link-color);text-decoration:var(--ifm-link-decoration)}.button--link.button--active,.button--link:active,.button--link:hover{color:var(--ifm-link-hover-color);text-decoration:var(--ifm-link-hover-decoration)}.block.fifth .box a,.button a,.dropdown__link--active,.dropdown__link:hover,.menu__link:hover,.navbar__brand:hover,.navbar__link--active,.navbar__link:hover,.pagination-nav__link:hover,.pagination__link:hover,a:hover,a:visited{-webkit-text-decoration:none;text-decoration:none}.button.disabled,.button:disabled,.button[disabled]{opacity:.65;pointer-events:none}.button--sm{--ifm-button-size-multiplier:0.8}.button--lg{--ifm-button-size-multiplier:1.35}.button--block{display:block;width:100%}.button.button--secondary{color:var(--ifm-color-gray-900)}:where(.button--primary){--ifm-button-background-color:var(--ifm-color-primary);--ifm-button-border-color:var(--ifm-color-primary)}:where(.button--primary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-primary-dark);--ifm-button-border-color:var(--ifm-color-primary-dark)}.button--primary.button--active,.button--primary:active{--ifm-button-background-color:var(--ifm-color-primary-darker);--ifm-button-border-color:var(--ifm-color-primary-darker)}:where(.button--secondary){--ifm-button-background-color:var(--ifm-color-secondary);--ifm-button-border-color:var(--ifm-color-secondary)}:where(.button--secondary):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-secondary-dark);--ifm-button-border-color:var(--ifm-color-secondary-dark)}.button--secondary.button--active,.button--secondary:active{--ifm-button-background-color:var(--ifm-color-secondary-darker);--ifm-button-border-color:var(--ifm-color-secondary-darker)}:where(.button--success){--ifm-button-background-color:var(--ifm-color-success);--ifm-button-border-color:var(--ifm-color-success)}:where(.button--success):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-success-dark);--ifm-button-border-color:var(--ifm-color-success-dark)}.button--success.button--active,.button--success:active{--ifm-button-background-color:var(--ifm-color-success-darker);--ifm-button-border-color:var(--ifm-color-success-darker)}:where(.button--info){--ifm-button-background-color:var(--ifm-color-info);--ifm-button-border-color:var(--ifm-color-info)}:where(.button--info):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-info-dark);--ifm-button-border-color:var(--ifm-color-info-dark)}.button--info.button--active,.button--info:active{--ifm-button-background-color:var(--ifm-color-info-darker);--ifm-button-border-color:var(--ifm-color-info-darker)}:where(.button--warning){--ifm-button-background-color:var(--ifm-color-warning);--ifm-button-border-color:var(--ifm-color-warning)}:where(.button--warning):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-warning-dark);--ifm-button-border-color:var(--ifm-color-warning-dark)}.button--warning.button--active,.button--warning:active{--ifm-button-background-color:var(--ifm-color-warning-darker);--ifm-button-border-color:var(--ifm-color-warning-darker)}:where(.button--danger){--ifm-button-background-color:var(--ifm-color-danger);--ifm-button-border-color:var(--ifm-color-danger)}:where(.button--danger):not(.button--outline):hover{--ifm-button-background-color:var(--ifm-color-danger-dark);--ifm-button-border-color:var(--ifm-color-danger-dark)}.button--danger.button--active,.button--danger:active{--ifm-button-background-color:var(--ifm-color-danger-darker);--ifm-button-border-color:var(--ifm-color-danger-darker)}.button-group{display:inline-flex;gap:var(--ifm-button-group-spacing)}.button-group>.button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.button-group>.button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.button-group--block{display:flex;justify-content:stretch}.button-group--block>.button{flex-grow:1}.card{background-color:var(--ifm-card-background-color);border-radius:var(--ifm-card-border-radius);box-shadow:var(--ifm-global-shadow-lw);display:flex;flex-direction:column;overflow:hidden}.card--full-height{height:100%}.card__image{padding-top:var(--ifm-card-vertical-spacing)}.card__image:first-child{padding-top:0}.card__body,.card__footer,.card__header{padding:var(--ifm-card-vertical-spacing) var(--ifm-card-horizontal-spacing)}.block.last,.card__body:not(:last-child),.card__footer:not(:last-child),.card__header:not(:last-child){padding-bottom:0}.card__body>:last-child,.card__footer>:last-child,.card__header>:last-child{margin-bottom:0}.card__footer{margin-top:auto}.table-of-contents{font-size:.8rem;margin-bottom:0;padding:var(--ifm-toc-padding-vertical) 0}.table-of-contents,.table-of-contents ul{list-style:none;padding-left:var(--ifm-toc-padding-horizontal)}.table-of-contents li{margin:var(--ifm-toc-padding-vertical) var(--ifm-toc-padding-horizontal)}.table-of-contents__left-border{border-left:1px solid var(--ifm-toc-border-color)}.table-of-contents__link{color:var(--ifm-toc-link-color);display:block}.table-of-contents__link--active,.table-of-contents__link--active code,.table-of-contents__link:hover,.table-of-contents__link:hover code{color:var(--ifm-color-primary);-webkit-text-decoration:none;text-decoration:none}.close{color:var(--ifm-color-black);float:right;font-size:1.5rem;font-weight:var(--ifm-font-weight-bold);opacity:.5;padding:1rem;transition:opacity var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.close:hover{opacity:.7}.close:focus{opacity:.8}.dropdown{display:inline-flex;font-weight:var(--ifm-dropdown-font-weight);position:relative;vertical-align:top}.button,.call-to-action a,.searchbox__input,.searchbox__submit,.searchbox__submit svg{vertical-align:middle}.dropdown--hoverable:hover .dropdown__menu,.dropdown--show .dropdown__menu{opacity:1;pointer-events:all;transform:translateY(-1px);visibility:visible}#nprogress,.dropdown__menu,.navbar__item.dropdown .navbar__link:not([href]){pointer-events:none}.dropdown--right .dropdown__menu{left:inherit;right:0}.dropdown--nocaret .navbar__link:after{content:none!important}.dropdown__menu{background-color:var(--ifm-dropdown-background-color);border-radius:var(--ifm-global-radius);box-shadow:var(--ifm-global-shadow-md);left:0;max-height:80vh;min-width:10rem;opacity:0;overflow-y:auto;padding:.5rem;position:absolute;top:calc(100% - var(--ifm-navbar-item-padding-vertical) + .3rem);transform:translateY(-.625rem);transition-duration:var(--ifm-transition-fast);transition-property:opacity,transform,visibility;transition-timing-function:var(--ifm-transition-timing-default);visibility:hidden;z-index:var(--ifm-z-index-dropdown)}.menu__caret,.menu__link,.menu__list-item-collapsible{border-radius:.25rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.dropdown__link{border-radius:.25rem;color:var(--ifm-dropdown-link-color);display:block;font-size:.875rem;margin-top:.2rem;padding:.25rem .5rem;white-space:nowrap}.dropdown__link--active,.dropdown__link:hover{background-color:var(--ifm-dropdown-hover-background-color);color:var(--ifm-dropdown-link-color)}.dropdown__link--active,.dropdown__link--active:hover{--ifm-dropdown-link-color:var(--ifm-link-color)}.dropdown>.navbar__link:after{border-color:currentcolor #0000;border-style:solid;border-width:.4em .4em 0;content:"";margin-left:.3em;position:relative;top:2px;transform:translateY(-50%)}.footer{background-color:var(--ifm-footer-background-color);color:var(--ifm-footer-color);padding:var(--ifm-footer-padding-vertical) var(--ifm-footer-padding-horizontal)}.footer--dark{--ifm-footer-background-color:#303846;--ifm-footer-color:var(--ifm-footer-link-color);--ifm-footer-link-color:var(--ifm-color-secondary);--ifm-footer-title-color:var(--ifm-color-white)}.footer__links{margin-bottom:1rem}.footer__link-item{color:var(--ifm-footer-link-color);line-height:2}.footer__link-item:hover{color:var(--ifm-footer-link-hover-color)}.footer__link-separator{margin:0 var(--ifm-footer-link-horizontal-spacing)}.footer__logo{margin-top:1rem;max-width:var(--ifm-footer-logo-max-width)}.docItemContainer_c0TR article>:first-child,.docItemContainer_c0TR header+*,.footer__item,h3{margin-top:0}.footer__title{color:var(--ifm-footer-title-color);font:700 var(--ifm-h4-font-size)/var(--ifm-heading-line-height) var(--ifm-font-family-base);margin-bottom:var(--ifm-heading-margin-bottom)}.menu,.navbar__link{font-weight:var(--ifm-font-weight-semibold)}.markdown p,p{line-height:165%}.admonitionContent_BuS1>:last-child,.collapsibleContent_i85q p:last-child,.details_lb9f>summary>p:last-child,.footer__items{margin-bottom:0}.footer-nav-links,.slick-slide,[type=checkbox],pre[data-theme]{padding:0}.hero{align-items:center;background-color:var(--ifm-hero-background-color);color:var(--ifm-hero-text-color);display:flex;padding:4rem 2rem}.hero--primary{--ifm-hero-background-color:var(--ifm-color-primary);--ifm-hero-text-color:var(--ifm-font-color-base-inverse)}.hero--dark{--ifm-hero-background-color:#303846;--ifm-hero-text-color:var(--ifm-color-white)}.hero__title{font-size:3rem}.hero__subtitle{font-size:1.5rem}.menu__list{margin:0;padding-left:0}.menu__caret,.menu__link{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu__list .menu__list{flex:0 0 100%;margin-top:.25rem;padding-left:var(--ifm-menu-link-padding-horizontal)}.menu__list-item:not(:first-child){margin-top:.25rem}.menu__list-item--collapsed .menu__list{height:0;overflow:hidden}.details_lb9f[data-collapsed=false].isBrowser_bmU9>summary:before,.details_lb9f[open]:not(.isBrowser_bmU9)>summary:before,.menu__list-item--collapsed .menu__caret:before,.menu__list-item--collapsed .menu__link--sublist:after{transform:rotate(90deg)}.menu__list-item-collapsible{display:flex;flex-wrap:wrap;position:relative}.menu__caret:hover,.menu__link:hover,.menu__list-item-collapsible--active,.menu__list-item-collapsible:hover{background:var(--ifm-menu-color-background-hover)}.menu__list-item-collapsible .menu__link--active,.menu__list-item-collapsible .menu__link:hover{background:none!important}.menu__caret,.menu__link{align-items:center;display:flex}.navbar-sidebar,.navbar-sidebar__backdrop{opacity:0;transition-duration:var(--ifm-transition-fast);transition-timing-function:ease-in-out;top:0;left:0;bottom:0;visibility:hidden}.menu__link{color:var(--ifm-menu-color);flex:1;line-height:1.25}.menu__link:hover{color:var(--ifm-menu-color)}.menu__caret:before,.menu__link--sublist-caret:after{filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast) linear;width:1.25rem;content:""}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem;margin-left:auto;min-width:1.25rem}.navbar__items--center .navbar__brand,body{margin:0}.menu__link--active,.menu__link--active:hover{color:var(--ifm-menu-color-active)}.navbar__brand,.navbar__link{color:var(--ifm-navbar-link-color)}.menu__link--active:not(.menu__link--sublist){background-color:var(--ifm-menu-color-background-active)}.menu__caret:before{background:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem}.navbar--dark,html[data-theme=dark]{--ifm-menu-link-sublist-icon-filter:invert(100%) sepia(94%) saturate(17%) hue-rotate(223deg) brightness(104%) contrast(98%)}.navbar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-navbar-shadow);height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.flex,.navbar>.container,.navbar>.container-fluid{display:flex}.navbar--fixed-top{position:sticky;top:0;z-index:var(--ifm-z-index-fixed)}.navbar__inner{display:flex;flex-wrap:wrap;justify-content:space-between;width:100%}.navbar__brand{align-items:center;display:flex;margin-right:1rem;min-width:0}.navbar__brand:hover{color:var(--ifm-navbar-link-hover-color)}.announcementBarContent_xLdY,.navbar__title{flex:1 1 auto}.navbar__toggle{display:none;margin-right:.5rem}.navbar__logo{flex:0 0 auto;margin-right:.5rem}.full-height,.navbar__logo img,body,html{height:100%}.navbar__items{align-items:center;display:flex;flex:1;min-width:0}.navbar__items--center{flex:0 0 auto}.navbar__items--center+.navbar__items--right{flex:1}.navbar__items--right{flex:0 0 auto;justify-content:flex-end}.navbar__item{display:inline-block;padding:var(--ifm-navbar-item-padding-vertical) var(--ifm-navbar-item-padding-horizontal)}.navbar__link--active,.navbar__link:hover{color:var(--ifm-navbar-link-hover-color)}.navbar--dark,.navbar--primary{--ifm-menu-color:var(--ifm-color-gray-300);--ifm-navbar-link-color:var(--ifm-color-gray-100);--ifm-navbar-search-input-background-color:#ffffff1a;--ifm-navbar-search-input-placeholder-color:#ffffff80;color:var(--ifm-color-white)}.navbar--dark{--ifm-navbar-background-color:#242526;--ifm-menu-color-background-active:#ffffff0d;--ifm-navbar-search-input-color:var(--ifm-color-white)}.navbar--primary{--ifm-navbar-background-color:var(--ifm-color-primary);--ifm-navbar-link-hover-color:var(--ifm-color-white);--ifm-menu-color-active:var(--ifm-color-white);--ifm-navbar-search-input-color:var(--ifm-color-emphasis-500)}.navbar__search-input{appearance:none;background:var(--ifm-navbar-search-input-background-color) var(--ifm-navbar-search-input-icon) no-repeat .75rem center/1rem 1rem;border:none;border-radius:2rem;color:var(--ifm-navbar-search-input-color);cursor:text;display:inline-block;font-size:1rem;height:2rem;padding:0 .5rem 0 2.25rem}.navbar__search-input::placeholder{color:var(--ifm-navbar-search-input-placeholder-color)}.navbar-sidebar{background-color:var(--ifm-navbar-background-color);box-shadow:var(--ifm-global-shadow-md);position:fixed;transform:translate3d(-100%,0,0);transition-property:opacity,visibility,transform;width:var(--ifm-navbar-sidebar-width)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar__items{transform:translateZ(0)}.navbar-sidebar--show .navbar-sidebar,.navbar-sidebar--show .navbar-sidebar__backdrop{opacity:1;visibility:visible}.navbar-sidebar__backdrop{background-color:#0009;position:fixed;right:0;transition-property:opacity,visibility}.navbar-sidebar__brand{align-items:center;box-shadow:var(--ifm-navbar-shadow);display:flex;flex:1;height:var(--ifm-navbar-height);padding:var(--ifm-navbar-padding-vertical) var(--ifm-navbar-padding-horizontal)}.navbar-sidebar__items{display:flex;height:calc(100% - var(--ifm-navbar-height));transition:transform var(--ifm-transition-fast) ease-in-out}.navbar-sidebar__items--show-secondary{transform:translate3d(calc((var(--ifm-navbar-sidebar-width))*-1),0,0)}.navbar-sidebar__item{flex-shrink:0;padding:.5rem;width:calc(var(--ifm-navbar-sidebar-width))}.navbar-sidebar__back{background:var(--ifm-menu-color-background-active);font-size:15px;font-weight:var(--ifm-button-font-weight);margin:0 0 .2rem -.5rem;padding:.6rem 1.5rem;position:relative;text-align:left;top:-.5rem;width:calc(100% + 1rem)}.navbar-sidebar__close{display:flex;margin-left:auto}.pagination{column-gap:var(--ifm-pagination-page-spacing);display:flex;font-size:var(--ifm-pagination-font-size);padding-left:0}.pagination--sm{--ifm-pagination-font-size:0.8rem;--ifm-pagination-padding-horizontal:0.8rem;--ifm-pagination-padding-vertical:0.2rem}.pagination--lg{--ifm-pagination-font-size:1.2rem;--ifm-pagination-padding-horizontal:1.2rem;--ifm-pagination-padding-vertical:0.3rem}.pagination__item{display:inline-flex}.pagination__item>span{padding:var(--ifm-pagination-padding-vertical)}.pagination__item--active .pagination__link{color:var(--ifm-pagination-color-active)}.pagination__item--active .pagination__link,.pagination__item:not(.pagination__item--active):hover .pagination__link{background:var(--ifm-pagination-item-active-background)}.pagination__item--disabled,.pagination__item[disabled]{opacity:.25;pointer-events:none}.pagination__link{border-radius:var(--ifm-pagination-border-radius);color:var(--ifm-font-color-base);display:inline-block;padding:var(--ifm-pagination-padding-vertical) var(--ifm-pagination-padding-horizontal);transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav{display:grid;grid-gap:var(--ifm-spacing-horizontal);gap:var(--ifm-spacing-horizontal);grid-template-columns:repeat(2,1fr)}.pagination-nav__link{border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-pagination-nav-border-radius);display:block;height:100%;line-height:var(--ifm-heading-line-height);padding:var(--ifm-global-spacing);transition:border-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pagination-nav__link:hover{border-color:var(--ifm-pagination-nav-color-hover)}.block.fifth .box a:hover,.button.button-empty:hover,.content_knG7 a,.markdown a,.navbar__link:hover,.package a:hover,.slider-content .slider-profile .company-link,.tag-cloud a:hover,.underline-link,p a,p a:hover,td a{-webkit-text-decoration:underline;text-decoration:underline}.pagination-nav__link--next{grid-column:2/3;text-align:right}.pagination-nav__label{font-size:var(--ifm-h4-font-size);font-weight:var(--ifm-heading-font-weight);word-break:break-word}.pagination-nav__link--prev .pagination-nav__label:before{content:"« "}.pagination-nav__link--next .pagination-nav__label:after{content:" »"}.pagination-nav__sublabel{color:var(--ifm-color-content-secondary);font-size:var(--ifm-h5-font-size);font-weight:var(--ifm-font-weight-semibold);margin-bottom:.25rem}.pills__item,.tabs{font-weight:var(--ifm-font-weight-bold)}.pills{display:flex;gap:var(--ifm-pills-spacing);padding-left:0}.pills__item{border-radius:.5rem;cursor:pointer;display:inline-block;padding:.25rem 1rem;transition:background var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.pills__item--active{color:var(--ifm-pills-color-active)}.pills__item--active,.pills__item:not(.pills__item--active):hover{background:var(--ifm-pills-color-background-active)}.pills--block{justify-content:stretch}.pills--block .pills__item{flex-grow:1;text-align:center}.tabs{color:var(--ifm-tabs-color);display:flex;margin-bottom:0;overflow-x:auto;padding-left:0}.tabs__item{border-bottom:3px solid #0000;border-radius:var(--ifm-global-radius);cursor:pointer;display:inline-flex;padding:var(--ifm-tabs-padding-vertical) var(--ifm-tabs-padding-horizontal);transition:background-color var(--ifm-transition-fast) var(--ifm-transition-timing-default)}.tabs__item--active{border-bottom-color:var(--ifm-tabs-color-active-border);border-bottom-left-radius:0;border-bottom-right-radius:0;color:var(--ifm-tabs-color-active)}.tabs__item:hover{background-color:var(--ifm-hover-overlay)}.tabs--block{justify-content:stretch}.tabs--block .tabs__item{flex-grow:1;justify-content:center}html[data-theme=dark]{--ifm-color-scheme:dark;--ifm-color-emphasis-0:var(--ifm-color-gray-1000);--ifm-color-emphasis-100:var(--ifm-color-gray-900);--ifm-color-emphasis-200:var(--ifm-color-gray-800);--ifm-color-emphasis-300:var(--ifm-color-gray-700);--ifm-color-emphasis-400:var(--ifm-color-gray-600);--ifm-color-emphasis-600:var(--ifm-color-gray-400);--ifm-color-emphasis-700:var(--ifm-color-gray-300);--ifm-color-emphasis-800:var(--ifm-color-gray-200);--ifm-color-emphasis-900:var(--ifm-color-gray-100);--ifm-color-emphasis-1000:var(--ifm-color-gray-0);--ifm-background-color:#1b1b1d;--ifm-background-surface-color:#242526;--ifm-hover-overlay:#ffffff0d;--ifm-color-content:#e3e3e3;--ifm-color-content-secondary:#fff;--ifm-breadcrumb-separator-filter:invert(64%) sepia(11%) saturate(0%) hue-rotate(149deg) brightness(99%) contrast(95%);--ifm-code-background:#ffffff1a;--ifm-scrollbar-track-background-color:#444;--ifm-scrollbar-thumb-background-color:#686868;--ifm-scrollbar-thumb-hover-background-color:#7a7a7a;--ifm-table-stripe-background:#ffffff12;--ifm-toc-border-color:var(--ifm-color-emphasis-200);--ifm-color-primary-contrast-background:#102445;--ifm-color-primary-contrast-foreground:#ebf2fc;--ifm-color-secondary-contrast-background:#474748;--ifm-color-secondary-contrast-foreground:#fdfdfe;--ifm-color-success-contrast-background:#003100;--ifm-color-success-contrast-foreground:#e6f6e6;--ifm-color-info-contrast-background:#193c47;--ifm-color-info-contrast-foreground:#eef9fd;--ifm-color-warning-contrast-background:#4d3800;--ifm-color-warning-contrast-foreground:#fff8e6;--ifm-color-danger-contrast-background:#4b1113;--ifm-color-danger-contrast-foreground:#ffebec;--ifm-background-color:#0d0f18;--ifm-navbar-background-color:#0d0f18;--ifm-code-background:var(--bg-color-code)}:root{--docusaurus-progress-bar-color:var(--ifm-color-primary);--ifm-code-font-size:var(--fontSizes-s);--csstools-color-scheme--light: ;color-scheme:dark;--ifm-font-family-base:"Atkinson Hyperlegible Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono","DejaVu Sans Mono",monospace;--ifm-font-family-monospace:"Atkinson Hyperlegible Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono","DejaVu Sans Mono",monospace;--ifm-heading-margin-bottom:40px;--bg-color:#20293c;--bg-color-dark:#0d0f18;--bg-color-darkest:#0d0f18;--bg-color-code:#282330;--shiki-background:var(--bg-color-code);--shiki-foreground:#f8f8f2;--shiki-token-comment:#6272a4;--shiki-token-keyword:#bd93f9;--shiki-token-function:#50fa7b;--shiki-token-string:#f1fa8c;--shiki-token-string-expression:#ff79c6;--shiki-token-constant:#8be9fd;--shiki-token-parameter:#ffb86c;--shiki-token-punctuation:var(--shiki-foreground);--shiki-line-highlight:#0000004d;--fontSizes-xxs:0.6rem;--fontSizes-xs:0.75rem;--fontSizes-s:0.9rem;--fontSizes-m:1rem;--fontSizes-l:1.25rem;--fontSizes-xl:1.5rem;--fontSizes-2xl:1.75rem;--fontSizes-3xl:2rem;--fontSizes-4xl:2.25rem;--fontSizes-5xl:2.5rem;--fontSizes-6xl:4.5rem;--fontSizes-7xl:5rem;--space-between-blocks:0px;--fontColor-white:#fff;--fontColor-offwhite:#fff;--ifm-hero-text-color:#fff;--fontColor-gray:#d3d3d3;--color-top:#ed168f;--color-middle:#b2218b;--color-bottom:#752a8a;--docusaurus-announcement-bar-height:auto;--docusaurus-tag-list-border:var(--ifm-color-emphasis-300);--docusaurus-collapse-button-bg:#0000;--docusaurus-collapse-button-bg-hover:#0000001a;--doc-sidebar-width:300px;--doc-sidebar-hidden-width:30px}#nprogress .bar{background:var(--docusaurus-progress-bar-color);height:2px;left:0;position:fixed;top:0;width:100%;z-index:1031}#nprogress .peg{box-shadow:0 0 10px var(--docusaurus-progress-bar-color),0 0 5px var(--docusaurus-progress-bar-color);height:100%;opacity:1;position:absolute;right:0;transform:rotate(3deg) translateY(-4px);width:100px}@font-face{font-display:swap;font-family:Atkinson Hyperlegible Mono;font-style:normal;font-weight:100 900;src:url(data:font/truetype;charset=utf-8;base64,AAEAAAAVAQAABABQR0RFRg8z+AsAAAPYAAABG0dQT1MO3zOSAAAmPAAAEKBHU1VCgONl9gAADIAAAAUwSFZBUg4BFC4AAAHcAAAAL01WQVIo9B8kAAACRAAAADtPUy8ywUlB3gAAAtwAAABgU1RBVPRp0ywAAAM8AAAAnGF2YXKVkKABAAABsAAAACpjbWFwBf0fQgAAB/AAAASQZnZhcpD4aZIAAAKAAAAAXGdhc3AAAAAQAAABZAAAAAhnbHlm4AsJXQAAeYQAAFc0Z3ZhcuKq3acAADbcAABCqGhlYWQnQ/VkAAACDAAAADZoaGVhBlMEugAAAYwAAAAkaG10eCLNl70AABGwAAAFsmxvY2E/uyqSAAAE9AAAAvptYXhwAYoAngAAAWwAAAAgbmFtZcNt9pIAABdkAAAG8HBvc3QNYmXuAAAeVAAAB+dwcmVwaAaMhQAAAVwAAAAHuAH/hbAEjQAAAQAB//8ADwABAAABfABYAAcARAAEAAEAAAAAAAAAAAAAAAAAAgABAAEAAAPY/sQAAAJ4//7+OQJ6A+gAAAAAAAAAAAAAAAAAAAFdAAEAAAAAAAEACMAAwAAAAAAACqsIuhVVEgkgACAAKqsoujVVMglAAEAAAAAAAQAAAAAAGQAAABQAAAAAAAAAAAAAAAEAAAEAAAASAAEAAAAMAAEAAAAAAAEAAAAAAQAAAAIAQlSzwOdfDzz1AAMD6AAAAADiUBhjAAAAAONjmVX//v8YAnoDbwAAAAYAAgAAAAAAAAABAAAAAAAIAAIAHHN0cnMAAAAAdW5kcwAAAAAAAQAAAAwAAQAAABYAAQABAABAAEAAAAEAAAABAAAIAAABAAAAEAACAAEAFAAHAAh3Z2h0AMgAAADIAAADIAAAAAABAAEBAAAAyAAAAQIAAAEsAAABAwAAAZAAAAEEAAAB9AAAAQUAAAJYAAABBgAAArwAAAEHAAADIAAAAAQCeADIAAUAAAKKAlgAAABLAooCWAAAAV4AMAECAAACAAAJAAAAAAAAoAAAbwAAYAoAAAAAAAAAAE5PTkUAwAAgJmoD2P7EAAAD5AGbIAAAEwAAAAAB8AKcAAAAIAADAAEAAQAIAAIAAAAUAAgAAAAkAAJ3Z2h0AQAAAGl0YWwBCAABABAAHAAoADgARABQAFwAaAABAAAAAAEBAMgAAAABAAAAAAECASwAAAADAAAAAgEDAZAAAAK8AAAAAQAAAAABBAH0AAAAAQAAAAABBQJYAAAAAQAAAAABBgK8AAAAAQAAAAABBwMgAAAAAwABAAIBCQAAAAAAAQAAAAEAAwASAAAAAAAAAHwAAADEAAIAEQABABMAAQAWACQAAQAmACYAAQAoADYAAQA4AEgAAQBKAFEAAQBTAIEAAQCFAJMAAQCVAJ0AAQCfAKcAAQCpAMIAAQDEAOAAAQFAAUMAAwFFAUoAAwFMAVsAAwFcAWEAAQFjAWgAAQABAAMAAAAuAAAAJgAAABAAAgADAUABQwAAAUUBSgAEAUwBVwAKAAEAAgFYAVkAAQALAUABQgFFAUcBSQFMAU4BUAFSAVQBVgABAAAATQABAAAADAA5AAAAAQAA4+rr7/Dx9Pb4+fr7/P3+/wECAwQFBgcICQoLDA0ODxARExQWFxgbHB0eHyIjJCgqKywwMjQ1ODtGAAEAAQAAQABAAAAAAAAnAEEATABYAGQAcAB7AIcAugD0AQABIQFYAYkBlAGgAfoCBQIsAjgCaAKYAq8CugLGAtIC3gLpAvQDAAMxA0UDfgOKA5UDoAO3A9wD8QQgBCsENwRDBE4EWQRlBJQEtgTCBNwE5wT2BQEFDAUXBTAFTQVlBXAFfAWHBZMFxQXQBdwF6AXzBf8GRwZTBo8GsgbWBxUHPwdKB1YHnweqB7YIKQg0CHoIiwiXCNII3Qj9CQgJFAkgCSsJNwlDCYEJxwnbCfoKBQoRCh0KKApFClwKZwpzCn8KigqgCqsKtwrCCv4LCQsUCx8LKgs1C0ALlAufC6oMDAxADHAMewyGDOAM6w0hDWQNoQ3yDioONQ5ADksOVg5hDmwOdw7IDucPLA83D5QPnw/CD+wP9xAJEBQQHxAqEDUQQBBLEIcQyhDVEPAQ+xETER4RNxFCEU0RWBF7Ea4R0RHcEecR8hH9EjMSPhJJElQSXxJqErkSxBMeE1ATghPDE+IT7RP4FDkURBRPFLgUwxUHFSYVMRV5FYQVpxWyFb0VyBXTFd4V6RYkFi8WQxZiFm0WeBaDFo4WpxbJFtQW3xbqFvUXCxcWFyEXLBdnF5MXqBfkGBIYMhgyGDIYMhhOGGsYdxiDGLcY1xj3GToZfhmHGZAZmRm1GdIaAhoRGh8aKxo3GkMaTxppGoMauBrtGv4bDxsXG0kbfBuvG8wb6RwCHBscKxw7HE4cXByOHMQdKx2EHaEeCB5hHrYe3h8KHxcfKx9AH1wfkx/PIB4gdSC5IREhNSE+IUwhYCFsIYUhsSHEIeQh9iIJIiIiOyJWIpwirCLSIuUjMSNrI34jlyOsI+4kSCTGJN4lBCUNJSQlOyVSJWolgiWaJbIl2iXjJfsmEyYcJjImOyZWJl8miya3JuMm7Cb4JwEnHidRJ3YnmieiJ6onsie6J8InyifXJ98n5yfvJ/cn/ygHKA8oTihmKI8o1yjzKSspayl9KdUqDipRKl8qmSrHKx8rMytZK5oAAAAAAAIAAAADAAAAFAADAAEAAAAUAAQEfAAAAHwAQAAFADwALwA5AH4ArAC0AQcBEwEbASMBJwErATMBNwE+AUgBVQFbAWUBawF+AZICGwI3AscCyQLdAwQDCAMMAygDlAOpA7wDwB6FHp4e8yAJIBQgGiAeICIgJiAwIDogRCCsISIhLiICIg8iEiIVIhoiHiIrIkgiYCJlJcomav//AAAAIAAwADoAoACuALYBCgEWAR4BJgEqAS4BNgE5AUEBUAFYAV4BagFuAZICGAI3AsYCyQLYAwADBgMKAyYDlAOpA7wDwB6AHp4e8iAJIBMgGCAcICAgJiAwIDkgRCCsISIhLiICIg8iESIVIhkiHiIrIkgiYCJkJcomav//AAABOgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP9/AAD+aQAA/pkAAAAAAAAAAP4y/U/9O/0p/SYAAOG0AADg4ODpAAAAAAAA4MjhDuDU4THgd9/33/HfOt8qAADfEgAA3xnfDd7r3s0AANt12qgAAQB8AAAAmAEgATgBRAHmAfgCAgIMAg4CEAIaAhwCJgI0Aj4CRAJSAlQAAAJyAAACdgAAAnYCgAKIAowAAAAAAAAAAAAAAoYAAAKOAAAAAAKMApAClAAAAAAAAAAAAAAAAAAAAAAAAAKGAAAChgAAAAAAAAAAAoAAAAAAAAAA5wDvAQ8A+AEiAT0BFAEQAP8BAAD3ASgA6wD7AOoA+QDsAO0BLwEsAS4A8QETAAEADAANABIAFgAfACAAJAAmAC8AMQAzADgAOQA+AEcASQBKAE0AUwBXAGAAYQBmAGcAbAEDAPoBBAE2AP4BXgBwAHsAfACBAIUAjgCPAJMAlQCfAKIApACpAKoArwC4ALoAuwC+AMQAyADRANIA1wDYAN0BAQEbAQIBNQDoAPABIAEkASEBJQEcARYBXAEXAOEBCwE0ARgBZwEaATIBegF7AV8BFQDzAWgBeQDiAQwBdwF2AXgA8gAGAAIABAAKAAUACQALABAAHAAXABkAGgAsACgAKQAqABUAPQBCAD8AQABFAEEBKgBEAFsAWABZAFoAaABIAMMAdQBxAHMAeQB0AHgAegB/AIsAhgCIAIkAmwCXAJgAmQCEAK4AswCwALEAtgCyASsAtQDMAMkAygDLANkAuQDbAAcAdgADAHIACAB3AA4AfQARAIAADwB+ABMAggAUAIMAHQCMABsAigAeAI0AGACHACEAkAAjAJIAIgCRACUAlAAtAJwALgCdACsAlgAnAJ4AMgCjADQApQA2AKcANQCmADcAqAA6AKsAPACtADsArABDALQARgC3AEsAvABMAL0ATgC/AFAAwQBPAMAAVQDGAFQAxQBdAM4AXwDQAFwAzQBeAM8AYwDUAGkA2gBqAG0A3gBvAOAAbgDfAFEAwgBWAMcBYQFjAWQBXQFlAWkBZgFgAUUBRwFMAVQBVgFQAUIBQAFSAUkBTgBlANYAYgDTAGQA1QBrANwBCQEKAQUBBwEIAQYBHQEeAPYBOgEpASYBOwExATAAAQAAAAoAwgFOAAJERkxUAKBsYXRuAA4AlgAJQVpFIAB8Q0FUIABmQ1JUIAB8S0FaIAB8TU9MIABQTkxEIAA6Uk9NIABQVEFUIAB8VFJLIAB8AAD//wAIAAAAAQACAAMABwAIAAkACgAA//8ACAAAAAEAAgADAAYACAAJAAoAAP//AAgAAAABAAIAAwAFAAgACQAKAAD//wAIAAAAAQACAAMABAAIAAkACgAEAAAAAP//AAcAAAABAAIAAwAIAAkACgALYWFsdACEY2FzZQB+Y2NtcAB0ZnJhYwBubG9jbABobG9jbABibG9jbABcbG9jbABWb3JkbgBQc3VwcwBKemVybwBEAAAAAQAVAAAAAQAQAAAAAQASAAAAAQAOAAAAAQAKAAAAAQALAAAAAQAJAAAAAQARAAAAAwACAAUABwAAAAEAFAAAAAIAAAABABYDYANEAtACwAKoAk4CQAH2AkAB6AHOAZgBigF8AT4BLAEUANgAkABuAEIALgABAAAAAQAIAAEABgAKAAEAAQFqAAEAAAABAAgAAQAGAAEAAQANAPQBQAFCAUUBRwFJAUwBTgFQAVIBVAFWAVoAAQAAAAEACAACAA4ABADhAOIA4QDiAAEABAABAD4AcACvAAYAAAACACQACgADAAEANAABABIAAAABAAAAEwABAAIAPgCvAAMAAQAaAAEAEgAAAAEAAAATAAEAAgABAHAAAgABAWoBcwAAAAQAAAABAAgAAQAsAAIAFgAKAAEABAF4AAMA+QFuAAIADgAGAXYAAwD5AWwBdwADAPkBbgABAAIBawFtAAEAAAABAAgAAQAGAA4AAQADAWsBbAFtAAEAAAABAAgAAgAcAAIAMAChAAYAAAABAAgAAQAKAAIAJAASAAEAAgAvAJ8AAQAEAAEAlwABAAAAAQAAAA8AAQAEAAEAKAABAAAAAQAAAA8AAQAAAAEACAABAdAAAgABAAAAAQAIAAEBwgABAAYAAAABAAgAAQG0AAEACAACABYABgABADMAAQABADMAAQAAAA0AAQCkAAEAAQCkAAEAAAAMAAEAAAABAAgAAQAGAAEAAQAEAFAAVQDBAMYAAQAAAAEACAABAU4ABQAGAAAAAgAcAAoAAwABACQAAQCMAAAAAQAAAAgAAwAAAAEAegABABIAAQAAAAgAAQAMAUEBQwFGAUgBSgFNAU8BUQFTAVUBVwFbAAEAAAABAAgAAQBEAAEABgAAAAIALAAKAAMAAQASAAEANAAAAAEAAAAGAAIAAgABAG8AAADjAOQAbwADAAEAEgABABIAAAABAAAABgABAAwBQAFCAUUBRwFJAUwBTgFQAVIBVAFWAVoAAgAQAAEACgAAAAEAQAABAAgAAgCWAVoAAQAQAAEACgAAAAEAQAABAAYAEAAEAEIAKAAQABAAAAADAAAAAQASAAEARAABAAAABAABAAEAnQADAAAAAQASAAEALAABAAAAAwABAAIAlQCfAAMAAAABACwAAQASAAEAAAADAAEACwFAAUIBRQFHAUkBTAFOAVABUgFUAVYAAQABAJUAAwAAAAEACAABAAgAAQAOAAEAAQDzAAIA9AD1AAEAAAABAAgAAgA+ABwA4QAwAOIAUQBWAOEAmgChAOIAwgDHAPUBQQFDAUYBSAFKAU0BTwFRAVMBVQFXAVsBdAF5AXoBewABABwAAQAvAD4AUABVAHAAlQCfAK8AwQDGAPQBQAFCAUUBRwFJAUwBTgFQAVIBVAFWAVoBagFrAWwBbQJ4AGYCeAAjAngAIwJ4ACMCeAAjAngAIwJ4ACMCeAAjAngAIwJ4ACMCeAAjAngABQJ4AHQCeAA2AngANgJ4ADYCeAA2AngANgJ4AE0CeABNAngAFgJ4ABYCeACJAngAiQJ4AIkCeACJAngAiQJ4AIkCeACJAngAiQJ4AIkCeACJAngAKQJ4ACkCeAApAngAKQJ4AEECeAAKAngAjwJ4AAoCeACPAngAjwJ4AI8CeACPAngAjwJ4AI8CeACPAngARQJ4AEUCeABiAngAYgJ4AJMCeACTAngAkwJ4AJMCeAAfAngAMwJ4AFUCeABVAngAVQJ4AFUCeABVAngAKQJ4ACkCeAApAngAKQJ4ACkCeAApAngAKQJ4ACkCeAAIAngAdwJ4AHcCeAApAngAdgJ4AHYCeAB2AngASQJ4AEkCeABJAngASQJ4AEkCeABRAngAPwJ4AD8CeAA/AngAPwJ4AFUCeABVAngAVQJ4AFUCeABVAngAVQJ4AFUCeABVAngAVQJ4ADECeAANAngADQJ4AA0CeAANAngADQJ4ACMCeAAkAngAJAJ4ACQCeAAkAngAJAJ4ADYCeAA2AngANgJ4ADYCeABYAngAWAJ4AFgCeABYAngAWAJ4AFgCeABYAngAWAJ4AFgCeABYAngACgJ4AGQCeABlAngAZQJ4AGUCeABlAngAZQJ4ADsCeAAsAngAOwJ4AD0CeABUAngAVAJ4AFQCeABUAngAVAJ4AFQCeABUAngAVAJ4AFQCeABtAngAQgJ4AEICeABCAngAQgJ4AHICeAAxAngAcwJ4AHMCeABzAngAcwJ4AHMCeABzAngAcwJ4AHMCeABzAngAbgJ4AGQCeABkAngAZAJ4AJgCeACYAngAfAJ4AHwCeAB8AngAfAJ4AHwCeABHAngAcgJ4AHICeAByAngAcgJ4AHICeABGAngARgJ4AEYCeABGAngARgJ4AEYCeABGAngARgJ4AAQCeABkAngAZAJ4ADsCeACiAngAogJ4AKICeABuAngAbgJ4AG4CeABuAngAbgJ4AGICeACCAngAggJ4AIICeACCAngAdgJ4AHYCeAB2AngAdgJ4AHYCeAB2AngAdgJ4AHYCeAB2AngAQgJ4ACMCeAAjAngAIwJ4ACMCeAAjAngATgJ4AEICeABCAngAQgJ4AEICeABCAngAaQJ4AGkCeABpAngAaQJ4AKgCeACeAngAIwJ4AB0CeACBAngASQJ4AAACeAAAAngAAAJ4AQgCeAEIAngBCAJ4AQgCeAA2AngBDQJ4AQ0CeACGAngAcwJ4AQgCeAEIAngBCAJ4AJYCeACNAngAJgJ4ADwCeAA8AngAswJ4AGkCeAAjAnj//gJ4AMMCeACWAngAlwJ4AKMCeADUAngAowJ4AQgCeACwAngAsAJ4ALACeAEHAngBCAJ4AHQCeAB0AngA1AJ4ANQCeADNAngBIAJ4AGECeABhAngADwJ4ABYCeABlAngAYQJ4ABQCeAAUAngADAJ4AMICeAEhAngBIQJ4AGUCeABjAngAOwJ4AHoCeABTAngASQJ4ABwCeABnAngANQJ4AQgCeAAuAngARAJ4AEQCeABxAngARAJ4AEQCeABEAngAVQJ4AFUCeABaAngAWgJ4AE4CeABLAngASAJ4AEoCeABnAngADQJ4AJoCeABbAngAWgJ4AEYCeABXAngAEAJ4ABwCeABrAAAAwAAAAMAAAAEPAAABDwAAAQwAAAD3AAAA8QAAAPcAAADxAAAAugAAALoAAAETAAAAwAAAAMAAAADAAAAAwAAAAMkAAADJAAAA3QAAAN0AAACxAAAAsQAAAL0AAAC9AAAA/QAAAOgAAAD7AAAA+wJ4AMABDwD3APcAugDAALgAwADJAN0AsQC9AOgA+wBAAHgAYgBlAD8AaABnAGYAUgBJAEAANQA/AFsAIwEGAK4ArwAAAAAAGgE+AAMAAQQJAAABBgSsAAMAAQQJAAEASgRiAAMAAQQJAAIADgRUAAMAAQQJAAMAXAP4AAMAAQQJAAQASgRiAAMAAQQJAAUAGgPeAAMAAQQJAAYARgOYAAMAAQQJAAgAUgNGAAMAAQQJAAkApAKiAAMAAQQJAAsAQgJgAAMAAQQJAAwALgIyAAMAAQQJAA0BIgEQAAMAAQQJAA4ANgDaAAMAAQQJABAANACmAAMAAQQJABEAFACSAAMAAQQJABkAMABiAAMAAQQJAQAADABWAAMAAQQJAQEAFACSAAMAAQQJAQIACgBMAAMAAQQJAQMADgRUAAMAAQQJAQQADABAAAMAAQQJAQUAEAAwAAMAAQQJAQYACAAoAAMAAQQJAQcAEgAWAAMAAQQJAQgADAAKAAMAAQQJAQkACgAAAFIAbwBtAGEAbgBJAHQAYQBsAGkAYwBFAHgAdAByAGEAQgBvAGwAZABCAG8AbABkAFMAZQBtAGkAQgBvAGwAZABNAGUAZABpAHUAbQBMAGkAZwBoAHQAVwBlAGkAZwBoAHQAQQB0AGsAaQBuAHMAbwBuAEgAeQBwAGUAcgBsAGUAZwBpAGIAbABlAE0AbwBuAG8ARQB4AHQAcgBhAEwAaQBnAGgAdABBAHQAawBpAG4AcwBvAG4AIABIAHkAcABlAHIAbABlAGcAaQBiAGwAZQAgAE0AbwBuAG8AaAB0AHQAcABzADoALwAvAG8AcABlAG4AZgBvAG4AdABsAGkAYwBlAG4AcwBlAC4AbwByAGcAVABoAGkAcwAgAEYAbwBuAHQAIABTAG8AZgB0AHcAYQByAGUAIABpAHMAIABsAGkAYwBlAG4AcwBlAGQAIAB1AG4AZABlAHIAIAB0AGgAZQAgAFMASQBMACAATwBwAGUAbgAgAEYAbwBuAHQAIABMAGkAYwBlAG4AcwBlACwAIABWAGUAcgBzAGkAbwBuACAAMQAuADEALgAgAFQAaABpAHMAIABsAGkAYwBlAG4AcwBlACAAaQBzACAAYQB2AGEAaQBsAGEAYgBsAGUAIAB3AGkAdABoACAAYQAgAEYAQQBRACAAYQB0ADoAIABoAHQAdABwAHMAOgAvAC8AbwBwAGUAbgBmAG8AbgB0AGwAaQBjAGUAbgBzAGUALgBvAHIAZwBoAHQAdABwADoALwAvAGgAZQBsAGwAbwBhAHAAcABsAGkAZQBkAC4AYwBvAG0AaAB0AHQAcABzADoALwAvAHcAdwB3AC4AYgByAGEAaQBsAGwAZQBpAG4AcwB0AGkAdAB1AHQAZQAuAG8AcgBnAC8ARQBsAGwAaQBvAHQAdAAgAFMAYwBvAHQAdAAsACAATQBlAGcAYQBuACAARQBpAHMAdwBlAHIAdABoACwAIABMAGkAbgB1AHMAIABCAG8AbQBhAG4ALAAgAFQAaABlAG8AZABvAHIAZQAgAFAAZQB0AHIAbwBzAGsAeQAsACAATABlAHQAdABlAHIAcwAgAGYAcgBvAG0AIABTAHcAZQBkAGUAbgBBAHAAcABsAGkAZQBkACAARABlAHMAaQBnAG4AIABXAG8AcgBrAHMALAAgAEwAZQB0AHQAZQByAHMAIABmAHIAbwBtACAAUwB3AGUAZABlAG4AQQB0AGsAaQBuAHMAbwBuAEgAeQBwAGUAcgBsAGUAZwBpAGIAbABlAE0AbwBuAG8ALQBFAHgAdAByAGEATABpAGcAaAB0AFYAZQByAHMAaQBvAG4AIAAyAC4AMAAwADEAMgAuADAAMAAxADsATgBPAE4ARQA7AEEAdABrAGkAbgBzAG8AbgBIAHkAcABlAHIAbABlAGcAaQBiAGwAZQBNAG8AbgBvAC0ARQB4AHQAcgBhAEwAaQBnAGgAdABSAGUAZwB1AGwAYQByAEEAdABrAGkAbgBzAG8AbgAgAEgAeQBwAGUAcgBsAGUAZwBpAGIAbABlACAATQBvAG4AbwAgAEUAeAB0AHIAYQBMAGkAZwBoAHQAQwBvAHAAeQByAGkAZwBoAHQAIAAyADAAMgAwAC0AMgAwADIANAAgAFQAaABlACAAQQB0AGsAaQBuAHMAbwBuACAASAB5AHAAZQByAGwAZQBnAGkAYgBsAGUAIABNAG8AbgBvACAAUAByAG8AagBlAGMAdAAgAEEAdQB0AGgAbwByAHMAIAAoAGgAdAB0AHAAcwA6AC8ALwBnAGkAdABoAHUAYgAuAGMAbwBtAC8AZwBvAG8AZwBsAGUAZgBvAG4AdABzAC8AYQB0AGsAaQBuAHMAbwBuAC0AaAB5AHAAZQByAGwAZQBnAGkAYgBsAGUALQBuAGUAeAB0AC0AbQBvAG4AbwApAAIAAAAAAAD/sgAwAAAAAQAAAAAAAAAAAAAAAAAAAAABfAAAACQAyQECAMcAYgCtAQMBBABjAK4AkAAlACYA/QD/AGQBBQAnAQYBBwDpACgAZQEIAMgAygEJAMsBCgELACkAKgD4AQwBDQArAQ4ALAEPAMwAzQDOAPoAzwEQAREALQESAC4BEwAvARQBFQEWAOIAMAAxARcBGAEZAGYAMgDQANEAZwDTARoAkQCvALAAMwDtADQANQEbARwANgEdAOQA+wEeAR8ANwEgASEBIgA4ANQA1QBoANYBIwEkASUBJgA5ADoBJwEoASkBKgA7ADwA6wErALsBLAA9AS0A5gEuAEQAaQEvAGsAbABqATABMQBuAG0AoABFAEYA/gEAAG8BMgBHATMBAQDqAEgAcAE0AHIAcwE1AHEBNgE3AEkASgD5ATgBOQBLAToATADXAHQAdgB3ATsAdQE8AT0BPgBNAT8BQABOAUEATwFCAUMBRADjAFAAUQFFAUYBRwB4AFIAeQB7AHwAegFIAKEAfQCxAFMA7gBUAFUBSQFKAFYBSwDlAPwBTACJAFcBTQFOAU8AWAB+AIAAgQB/AVABUQFSAVMAWQBaAVQBVQFWAVcAWwBcAOwBWAC6AVkAXQFaAOcBWwCdAJ4BXAFdAV4AmwADAV8BYAARAA8AHQAeAKsABACjACIAogDDAWEBYgCHAA0ABgASAD8AEACyALMAQgALAAwAXgBgAD4AQADEAMUAtAC1ALYAtwCpAKoAvgC/AAUACgCmAWMAIwAJAIgAhgCLAIoAjACDAF8A6ACCAMIBZACEAL0ABwFlAIUAlgFmAWcADgDvAPAAuAAgAI8AIQAfAJUAlACTAKcApABhAEEAkgCcAJoAmQClAJgACADGALkBaAFpAWoBawFsAW0BbgFvAXABcQFyAXMBdAF1AXYBdwF4AXkBegF7AXwBfQF+AX8BgAGBAYIBgwCOANwAQwCNAN8A2AGEAOEA2wDdANkA2gDeAOAAEwAUABUAFgAXABgAGQAaABsAHAGFALwA9AD1APYBhgGHAYgGQWJyZXZlB0FtYWNyb24HQW9nb25lawpDZG90YWNjZW50BkRjYXJvbgZEY3JvYXQGRWNhcm9uCkVkb3RhY2NlbnQHRW1hY3JvbgdFb2dvbmVrB3VuaTAxMjIKR2RvdGFjY2VudARIYmFyAklKB0ltYWNyb24HSW9nb25lawt1bmkwMDRBMDMwMQd1bmkwMTM2BkxhY3V0ZQZMY2Fyb24HdW5pMDEzQgZOYWN1dGUGTmNhcm9uB3VuaTAxNDUNT2h1bmdhcnVtbGF1dAZSYWN1dGUGUmNhcm9uBlNhY3V0ZQd1bmkwMjE4B3VuaTFFOUUGVGNhcm9uB3VuaTAxNjIHdW5pMDIxQQ1VaHVuZ2FydW1sYXV0B1VtYWNyb24HVW9nb25lawVVcmluZwZXYWN1dGULV2NpcmN1bWZsZXgJV2RpZXJlc2lzBldncmF2ZQtZY2lyY3VtZmxleAZZZ3JhdmUGWmFjdXRlClpkb3RhY2NlbnQGYWJyZXZlB2FtYWNyb24HYW9nb25lawpjZG90YWNjZW50BmRjYXJvbgZlY2Fyb24KZWRvdGFjY2VudAdlbWFjcm9uB2VvZ29uZWsHdW5pMDEyMwpnZG90YWNjZW50BGhiYXIJaS5sb2NsVFJLB2ltYWNyb24HaW9nb25lawJpagd1bmkwMjM3C3VuaTAwNkEwMzAxB3VuaTAxMzcGbGFjdXRlBmxjYXJvbgd1bmkwMTNDBm5hY3V0ZQZuY2Fyb24HdW5pMDE0Ng1vaHVuZ2FydW1sYXV0BnJhY3V0ZQZyY2Fyb24Gc2FjdXRlB3VuaTAyMTkGdGNhcm9uB3VuaTAxNjMHdW5pMDIxQg11aHVuZ2FydW1sYXV0B3VtYWNyb24HdW9nb25lawV1cmluZwZ3YWN1dGULd2NpcmN1bWZsZXgJd2RpZXJlc2lzBndncmF2ZQt5Y2lyY3VtZmxleAZ5Z3JhdmUGemFjdXRlCnpkb3RhY2NlbnQHdW5pMDM5NAd1bmkwM0E5B3VuaTAzQkMHdW5pMDBBMAd1bmkyMDA5FnBlcmlvZGNlbnRlcmVkLmxvY2xDQVQbcGVyaW9kY2VudGVyZWQubG9jbENBVC5jYXNlC211c2ljYWxub3RlCWVzdGltYXRlZARFdXJvB3VuaTIyMTkHdW5pMjIxNQd1bmkwMzA4DHVuaTAzMDguY2FzZQd1bmkwMzA3DHVuaTAzMDcuY2FzZQ11bmkwMzA3LmxhcmdlCWdyYXZlY29tYg5ncmF2ZWNvbWIuY2FzZQlhY3V0ZWNvbWIOYWN1dGVjb21iLmNhc2UHdW5pMDMwQgx1bmkwMzBCLmNhc2ULdW5pMDMwQy5hbHQHdW5pMDMwMgx1bmkwMzAyLmNhc2UHdW5pMDMwQwx1bmkwMzBDLmNhc2UHdW5pMDMwNgx1bmkwMzA2LmNhc2UHdW5pMDMwQQx1bmkwMzBBLmNhc2UJdGlsZGVjb21iDnRpbGRlY29tYi5jYXNlB3VuaTAzMDQMdW5pMDMwNC5jYXNlB3VuaTAzMjYHdW5pMDMyNwd1bmkwMzI4DHVuaTAzMjguY2FzZQd1bmkwMkM5CXplcm8uemVybwd1bmkwMEI5B3VuaTAwQjIHdW5pMDBCMwAAAQAAAAoAJgBGAAJERkxUAA5sYXRuAA4ABAAAAAD//wACAAAAAQACbWFyawAWbWttawAOAAAAAgADAAQAAAADAAAAAQACAAULLAmMAJQAbgAMAAYAEAABAAoAAgABCWAJYAABCKwADAAWA6gIGgOoBwICfgBCAnQAOAJqAC4DsggkAmAGwAJWCC4CTAgAA5QH8AJCCBAAAwFpA2MO3AoYAAMBCANWCPAICgADAXADVg54CAAABgAQAAEACgABAAEP2g/aAAEPsAAMAAIABgw4AAMBP/8VBi4OfAAEAAAAAQAIAAEI2gfOAAEIJgAMAN0IwgeyB6gHngeUB7IHigjCB3oHagdgB1YHTAdCBzIHTAciBxIHCAb+BvQG6gbgBtYGzAb0BsIGuAauBqQGmgakBpAGhgjCB7IHngeUBnwHsgeKCMIGcgZoBlgGWAZOBkQGTgZOCMIIwgeyBjoIwgdqBjQGKgYgBhYGKgYGBjQF/AXyBegF3gXUBcoFwAjCB7IGOgjCCMIIwgY6CMIIwgjCB7IHngeUB7IFtgeKCMIFpgWcBZIFiAV+BXQFiAVqBWAFVgVMBUIFVgU4BS4FJAUaBRAFBgT8BOwE3AUGBMwFEATCBLgEsgSoBJ4ElASKBJ4EgAR2BGwEYgRYBE4ERAREBGIEOgQ0BCoEIAQWBAYD/APyA9wD0gPIA74DtAO0A8gDqgPcA5oDkAOGA3wDfANyA2gDcgNyA14IdANUA0oIdANACMgDNgMsAyIDNgMYCMgDDgMEAvoC8ALmAtYCzALCArgCrgKkArgCuAKaApoCmgKaAvACkAKGAnwCkAJyAmgC8AJeAlQCSgJAAjYCLAJAAiYIyAM2AywDIgM2AiACFgIMAgIDIgMiAfgB7gHkAywB2gHQAcYDDgG8AAMBPAKLDEgDGgADATwC+wAAB/oAAwE8AqQAAAxUAAMBPAKhAAADHAADAWkCtwxQB9wAAwEKArgAAAzmAAMBbwK4DawM3AADAUcCrwAAAuQAAwFHAqEAAALqAAMBRwK4AAAMvgABAUcB8AABAT0B8AADATwCrwxoAroAAwE8AqcMXgLAAAMBPAK4DFQMlAADATwB8AxKAAAAAwE9AfANUAAAAAMBOgL7C6YHYgADAToCiwucAm4AAwGTArcLOAdOAAMBOgKvC4gCagADAToCpwt+AnAAAwE6ArgLdAxEAAMBHQHwC+oAAAADAToCoQNsAlIAAwE6ArgDYgwmAAMBOgHwA1gAAAADATsCoQAeAjQAAwE7ArgAFAwIAAMBOwHwAAoAAAAAAB6AAAADAUwB8AteAAAAAwE6AfALFAAAAAMBEQHwCZgAAAADASsB8AmeAAAAAwE8ApQAAAR2AAMBlQK3AvgGqAADATwCrwAAAcQAAwE8AqcAAAHKAAMBPAK4AAALngADATwClArEBEQAAwE8AqEKugGsAAMBPAK4CrALgAADATgB8ApcAAAAAwEJA34BbgRUAAMBCQLEAWQAAAADAUQDHAsIAAAAAwFVArgAHgtOAAMBVQHwABQAAAADAVUCuAAKAEwAAAAlgAAAAwE3AosAQgEsAAMBNwKvADgBMgADATcCpwAuATgAAwE3ArgAJAsMAAMBNwHwABoAAAADATcCuAAQAAoAAAA4gAAAAAAkgAAAAwE2AxwJuAAAAAMBMwKvCe4A6gADAVEC8wAKAlwAAAAJgAAAAwEzAqQJ1AoOAAMBMwHwCcoAAAADASgDHAt6AAAAAQEFAfAAAwFGAosKagCcAAMBRgKvCmAAogADAUYCpwpWAKgAAwFGAqEKTACeAAMBRgK4CkIKcgADAUYB8Ao4AAAAAwFJAxwJzgAAAAMBWgKvCjQAZgADAVoCoQoqAGwAAwFaArgKIApAAAMBWgHwChYAAAADATAB8AjEAAAAAQE7AfAAAwE2ApQJAgLMAAMBNgL7CPgE/gADATYCiwjuAAoAAAAggAAAAwE2Aq8I3gAKAAAALoAAAAMBNgKnCM4ACgAAADWAAAADATYCpAi+CSgAAwE2ArgItAnOAAMBNgHwCKoAAAADAUADOwhCAhIAAwFAA0UIOAIYAAMBQANWCC4CjgADAUACnAgkAAAAAwE9AzcHvgU0AAMBPQNKB7QESgADAT0DVgeqAmYAAwE9ApwHoAAAAAMBPAKcCJoAAAADAT4DNweMBQIAAwE+A0oHggQYAAMBPgNWB3gCNAADAT4CnAduAAAAAwE9ApwKCAAAAAMBPANnAAAACgAAADaAAAADAZUDYwBaBAoAAwEkA0UI9AF8AAMBJANWCOoB8gADASQCnAjgAAAAAwEqApwH7AAAAAMBPQKcCNwAAAADATwCnAiiAAAAAwE9A0YAAAF4AAMBlgNjAAoDugAAABeAAAADAT0DNwAABGAAAwE9A0oAAAN2AAMBPQNWAAABkgABAT0CnAADATwDRQAAAQIAAwFAA1YIgAF4AAMBQAKcCHYAAAADATICnAAKAAAAAAAhgAAAAwHYA1YHBAFUAAMB2AKcBvoAAAADATwDOwAAALAAAwE7ApwIDgAAAAMBQwM7B+QAnAADAUMDSwfaByAAAwFDApwH0AAAAAMBNQKcB9YAAAADARkCnAC8AAAAAwFRAx4HUgMOAAMBUQM7B0gAYAADAVEDNwc+A6AAAwFRA0oHNAK2AAMBUQNFByoAUgADAVEDVgcgAMgAAwFRApwHFgAAAAMBSANFABQANAADAUgCnAAKAAAAAAACgAAAAwFRAzsHcgAKAAAAMoAAAAMBUQNFB2IACgAAADCAAAADAVEDVgdSAHoAAwFRApwHSAAAAAMBPAKcB24AAAADAToCnAd0AAAAAwE8A0YAAAAKAAAAGYAAAAMBPANDAAAACgAAACmAAAADATwDHgAAAkYAAwE8AzcAAALiAAMBPANKAAAB+AADATwDSwAABhIAAwE8A1YAAAAKAAAAKoAAAAIADgABABMAAAAWACQAEwAmACYAIgAoADYAIwA4AEgAMgBKAFEAQwBTAIEASwCFAJMAegCVAJ0AiQCfAKcAkgCpAMIAmwDEAOAAtQFcAWEA0gFjAWcA2AAWAAAArgAAAKgAAACuAAAAqAAAAJ4AAACUAAAAjgAAAH4AAAB0AAAAZAAAAK4AAACoAAAArgAAAKgAAACuAAAAqAAAAK4AAACoAAAArgAAAKgAAABaAAAAqAADATwB8AWQAAAAAwEQApwACgAAAAAAC4AAAAMBEAHwABQAAAADAQgCnAAKAAAAAAAMgAAAAQEKAfAAAwFwApwFhgAAAAMBbwHwBuwAAAABATwCnAABATwB8AACAAMBQAFDAAABRQFKAAQBTAFXAAoABAAAAAEACAABAZABJAABAXYADABJAQ4BDgEOAQ4BDgEOAQ4BDgEOAP4A/gD+AP4A/gD+AP4A/gDuAO4A7gDuAO4A7gDuAN4A3gDeAN4A3gDeAN4A3gDOAM4AzgDOAM4AzgDOAM4AzgC+AL4AvgC+AL4AvgC+AL4ArgCuAK4ArgCuAK4ArgCuAKQApACkAKQApACkAKQApACUAJQAlACUAJQAlACUAJQAAwH/AAoACgAAAAAAMYAAAAMCKwAKBHgAAAADAhEACgAKAAAAAAA3gAAAAwHaAAoACgAAAAAALYAAAAMCAwAKAAoAAAAAADOAAAADAkoACgAKAAAAAAAAgAAAAwHpAAoACgAAAAAAK4AAAAMCGQAKAAoAAAAAACeAAAADAlUACgQuAAAAAgANAAEABwAAAAkACgAHABYAHQAJACYAJgARACgALQASAD4ARQAYAHAAdgAgAHgAeQAnAIUAjAApAJUAnAAxAK8AtgA5AMgAzgBBANAA0ABIAAIAAAAKAAAACgADAX0ACgAKAAAAAAA0gAAAAQACAVoBWwAEAAAAAQAIAAEFHgRUAAEE9AAMAL0EQgRCBEIEQgRCBEIEQgRCBEIEMgQiBBIEEgQSBBIEAgQCA/ID8gPyA/ID8gPyA/ID8gPiA9ID0gO8A9IDsgRCBEIEQgRCBEIEQgRCA6IDogOSA4IDcgNyA3IDYgRCA1gDWANYA0gDWANCA0IDQgNCA0IDQgNCA0ID8gM4AygDGAMYAxgEQgRCBEIDCARCBEIDCAL+AvQC9AL0AvQC9ALqA0IDQgNCA0IDQgLqAuoC6gLqAuAC4ALgAuAC4ALgAuAC4ALgAtoCygK6AroCugK6ArACpgKmAqYCpgKmAqYCpgKmApwCkgKSApICkgKIAngCeAJ4AngCeAJ4AngCeAJuAm4CbgJeAlQCRAJEAkQCNAIqAiACIAIgAhACIARCBEIEQgRCBEIEQgRCBEICAAHwAeYB3AHSAdIB0gHIAcgByAG+AbQBtAGqAaABoAGgAaABoAGgAaABoAL+A7IDsgOyA7IDsgNCAZYBlgGWAZYBlgGMAYwBjAGMAXwAAwFC/04DgAAKAAAAB4AAAAMBNwAAApAAAAADATkAAAJWAAAAAwEvAAACjAAAAAMBRv8VARoCHAADAU8AAAH4AAAAAwE8/xUBZAIIAAMBRQAAAPwAAAADAL0AAAHKAAAAAwFMAAAB0AAAAAMBOgAAAYYAAAADAREAAAAKAAAAAAAvgAAAAwErAAAACgAAAAAAI4AAAAMBLf8VAAoBtgAAAB+AAAADATYAAAHsAAAAAwE9AAABQgAAAAMBaf8VAAoBkgAAACiAAAADAXIAAAAKAAAAAAAigAAAAwFA/xUBuAFyAAMBSQAAAAoAAAAAAA6AAAADAVX/BQEuAAAAAwE8AAAACgAAAAAAAYAAAAMBNgAAAIoAAAADATv/BQCQAAAAAwEoAAACcAAAAAMBOQAAAWYAAAADAUkAAAD8AAAAAwFYAAAACgAAAAAADYAAAAMBMAAAAAoAAAAAABqAAAABATsAAAADATYAAABCAAAAAwE8AAAAggAAAAMBPgAAAhgAAAADAT0AAAIOAAAAAwEz/xUACgC+AAAAGIAAAAMBNwAAAAoAAAAAABWAAAADASoAAAAKAAAAAAAmgAAAAwE9AAAA9AAAAAEBPQAAAAMBI/8VAAoAfgAAAB2AAAADASwAAADEAAAAAwFL/xUACgBkAAAAEIAAAAMBVAAAAAoAAAAAAAiAAAADASn/FQAKAEQAAAAcgAAAAwEyAAAACgAAAAAAE4AAAAMBQgAAAAoAAAAAAAaAAAADATsAAABKAAAAAwE6/xUAEAAKAAAABIAAAAAACoAAAAMBQwAAAAoAAAAAAAWAAAADATUAAAAKAAAAAAASgAAAAwE8AAAACgAAAAAAEYAAAAMBPgAAAAoAAAAAABaAAAADAU4AAAAKAAAAAAAUgAAAAwE8AAAACgAAAAAAG4AAAAMBOgAAAAoAAAAAACyAAAABATwAAAACABoAAQAHAAAACQAPAAcAEQATAA4AFgAdABEAHwAkABkAJgAmAB8AKAAtACAALwA2ACYAOABIAC4ASgBPAD8AUQBRAEUAUwBUAEYAVgBWAEgAYAB2AEkAeAB+AGAAgACBAGcAhQCMAGkAjgCTAHEAlQCcAHcAnwCnAH8AqQDAAIgAwgDCAKAAxADFAKEAxwDOAKMA0ADgAKsBaAFoALwAAgAAABoAAAAKAAMBQgAAAAoAAAAAAAOAAAADAUgAAAAKAAAAAAAPgAAAAQACAVgBWQABAAAAAQABAAADDgF8AAAAAAMQAAAAAAARABgAHwAmAC0ANAA7AGAAjACTAKsA1gD6AQEBCQFOAVUBdAF8AaQBzAHbAeIB6gHyAfoCAQIIAhACNgJEAnACeAJ/AoYClQKtArwC4ALnAu4C9QL7AwIDCQMwA0oDUQNiA2kDdAN7A4IDiQOdA7IDxAPLA9ID2QPgBAUEDAQTBBoEIQQoBFsEYgSSBK0EywT5BRgFHwUnBV4FZQVsBcUFzAYABgwGEwZCBkkGYgZpBnAGdwZ+BoUGjAa9BvIG/gcUBxsHIwcrBzIHRgdWB10HZQdtB3QHhgeNB5UHnAfKB9EH2AffB+YH7Qf0CDUIPAhDCI0ItAjZCOAI5wktCTQJXQmPCcAJ/AolCiwKMwo6CkEKSApPClYKkwquCuQK6wsxCzgLUwt3C34LjAuTC5oLoQuoC68LtgvnDBsMIgw4DD8MUAxXDGwMcwx7DIIMnwzHDOIM6QzwDPcM/g0oDS4NNA06DUENSA2ADYYNyQ3vDhUORg5eDmUObA6dDqQOqw79DwQPOg9TD1sPlA+bD7UPvA/DD8oP0Q/YD94QCxASEB4QMhA5EEAQRxBOEGIQexCBEIcQjRCUEKMQqRCvELUQ4hEBEQ4RQBFjEX0RfRF9EX0RkRGoEa8RtxHgEfgSEBJEEngSfxKGEo0SjRKiEskS0RLZEuIS6xL0EvwTEBMkE08TehOGE5ITmBO/E+sUFRQvFEcUUxRfFGgUcRSAFIsUtRTeFTAVcxWFFdEWEhZRFm0WixaTFp0WrBbBFuoXFxdSF5MXyBgNGCsYMhg6GEkYUhhlGIYYkhisGLoYyBjZGOoY/BkzGT4ZXRloGaIZ0RndGe8Z/Ro0GmkatxrGGtga3xruGv0bDxsiGzUbSBtbG3obgRuTG6YbrRu+G8Ub2hvhG/scHBw/HEYcTxxWHG0clRyyHM8c1RzbHOEc5xztHPMc/B0CHQgdDh0UHRodIB0mHVQdaB2JHb8d0h38Hi0eOh59Hqke3h7mHxAfMB9wH34fnB/MQACAAQAIABgAAAAL/MM8BKC/OVlMrfz8g4QGysoAIyOOjoMAgAEACAAFAAAAgAAEg4WAAQAIAAUAAACFgAD5g4ABAAgABQAAAIWAAPeDgAEACAAFAAAAhYAACoOAAQAIAAUAAACAAPyDhYABAAgABQAAAIWAACGDgAEACABBAAAAH/zDPAQMBvn5+fPx9v8EBAnq4r2hnZ2doqG/OVlMrfz8g4MbBxAUFBsgICAhIvj7+Pj4BQ8KBwP/ysoAIyOOjoOAAQAIAE4AAAAk/L/I1tbW4fT/Ch0qKio3QASgvzlZTK38/AD99/f3/QADCQkJA4OADu729/UBFB4eHhQB9ff374ESysoAIyOOjv399/Tx6+vr8fT3/YMAgAEACAAFAAAAhYAA9YOAAQAIABkAAAwLAAECAgICAgIBAQEDgArRC0zdTAgCN1NIAoEJrijXUgDU1AAZogCAAQAIAE0AAAAlzs76AxkpKSknKC4gHiUlJQ3x7jH54Me+vr7X+zEx9tXExMTgCDGDgwwJC/75/wcGCxQVDvj1gRJWVlY/IBL90dHRJycnBO7Wqqqqg4ABAAgAPwAAAB4FAP3+/v78+wAKAwOvtd0FHEtqampMIAroxbi0AwQGg4EE+Pb+BASCE/X118+pqam53/4aQldXV0EqKAsOhIABAAgABQAAAIAABoOFgAEACAAEAAABAAEAAgD2AIABAAgAgQAAAD4D/fz8/vD3+/D29vbz+f8ICQL5+fz6+/7+/vz7AAoDA6+13QUcS2pqakwgCujFuLQDBQ0SEQ0NFBIgMDAwGgSDGvX17ukECgkJCff8+/j4+Pv59fr8AQL8+P4EBIIg9fXXz6mpqbnf/hpCV1dXQSooCxAFAf///v7+AwL35+n1g4ABAAgABQAAAIAAAoOFgAEACAA1AAAAGfHxEQsFBQYGBgUFCxFUFQDWtJ+fn7TWABVUg4MGAgQB/vn6/IEMWlpaUTwZ/uPCsKioqIOAAQAIAAQAAAEAAQDrAPYAgAEACABHAAAAIfHx+/vx8RELBQUGBgYFBQsRVBUA1rSfn5+01gAVVFTq6lSDgAPLyyYmggYCBAH++fr8gRBaWlpRPBn+48KwqKioJibLy4OAAQAIAEcAAAAh8fH7+/HxEQsFBQYGBgUFCxFUFQDWtJ+fn7TWABVUVOrqVIOAA8vLJiaCBgIEAf75+vyBEFpaWlE8Gf7jwrCoqKgmJsvLg4ABAAgADgAABgUBAgICAgIFzxxB2UEcgASaMs1mAIABAAgABQAAAIAA/IOFgAEACAAEAAABAAEA+AD2AIABAAgABAAAAQABAPgA9wCAAQAIAAQAAAEAAQD4AAoAgAEACAAFAAAAgAD4g4WAAQAIAAUAAACAAPSDhYABAAgABAAAAQABAPgAIQCAAQAIAEMAAAAgz88cHEFB2dlBQRwcJB4RERELCQ4XHBwhAvrVubW1tbm4g4IcmpoyMs3NZmYABxAUFBsgICAhIvj7+Pj4BQ8KBwOEgAEACAAMAAAFBAECAgICBNQcRhxGgAOaD6kAAIABAAgATwAAACbq8fn7+/v+Af/+AwCuu+oFHkliYmJJIg4H5sKqqqr9/QMDzcTL3euDgQT5+QIEA4IWAwTnxampqcDoAhY+V1dXUDoR8OrqOTmBAuby/YSAAQAIAAQAAAEAAQDxAPkAgAEACAAFAAAAgADyg4WAAQAIAAUAAACAAPGDhYABAAgADgAABgUBAgICAgIF8mScDpxkgAAxgQHLAIABAAgAGgAADAsBAgICAgICAgICAgIL8vzyZJwOBA6cZJxkC+0fAB8AH+0AywAx7YABAAgADgAABgUBAgICAgIF3sfeIjkiBVKuAK5SAIABAAgAPgAAAB0VHSUNU1hEIxQL7dTU1CgoKCn7+93d+/sTEzExExODgQrpyvAVO0hISEAjBIEBGhiBA1JSrq6BA66uUlKEAIABAAgABQAAAIAABIOFgAEACAAFAAAAhYAA94OAAQAIAAUAAACFgAAKg4ABAAgAAgAAAIWFAIABAAgABQAAAIAA/IOFgAEACAAFAAAAhYAAIYOAAQAIAEUAAAAg3t7Hx97eIiI5OSIiKiQXFxcRDxQdIiInCADbv7u7u7++g4ADUlKuroEYrq5SUgAHEBQUGyAgICEi+Pv4+PgFDwoHA4SAAQAIACoAAAAT/fDn2cctOyoI/wT449LS0kRERC+DgQwD+OgYOVJWVlZaXVhQgQFRM4QAgAEACAAFAAAAgAAPg4WAAQAIABkAAAAL19c6Or0tPRGb+jo6g4IAeYEA+oEBp+iEgAEACAAFAAAAgAAFg4WAAQAIAAgAAAMCAQICAtJEAoABZgAAgAEACAAFAAAAgAAQg4WAAQAIAAUAAACAAECDhYABAAgABQAAAIAA+YOFgAEACAAfAAAADdLS///S0kREEBBERAICg4AD2/U+JIEFak4FIWZmhIABAAgAIQAAAA/w8FoBAa4QEMzMzBzfMjIyg4JBAIkAiYMBlpaBAZaWhIABAAgAGwAAAAve3j3MzMwiIsI0NDSDgkEAmgCag0H/Zv9mhIABAAgABQAAAIAABIOFgAEACAAFAAAAhYAA9oOAAQAIAAUAAACAAAaDhYABAAgABQAAAIWAAPWDgAEACABAAAAAgQT++/v7/oIEAgUFBQKBDua2mZmZtuYAG0pnZ2dKG4OBBPr6AAcGghUGBwD6+gBXV0EaAOW/qampv+UAGkFXgwCAAQAIAAUAAACAAASDhYABAAgABQAAAIWAAPeDgAEACAAFAAAAhYAACoOAAQAIAAUAAACAAPyDhYABAAgABQAAAIAABIOFgAEACABcAAAAgAkMGh0yEP77+/v+gQr05OLM7gICBQUFAoES5raZmZmrvFAxQ6/NABtKZ2dnVIOBCPDi9gr6+AAHBoIeER8K9QcHBQD6+gBXV0EaAN+smjJXZ8+pqam/5QAiV4MAgAEACAAFAAAAhYAA9YOAAQAIAFYAAAApBwYB/f39AQUG4dzcDw8eHt/fHh4ODtzc3/QVA+nc3NzpAxUmQE1NTUAmg4EE+/sABQWCADyBB66uKCjX11JSgRLF4QBISC4MAfTRuLi40fQBDi9IgwCAAQAIAC0AAAAVy8sSEBwrKyscEBIuLi4T68TExOsTLoODEfzu4dPGwsLCABgYGAPhwKqqqoOAAQAIADIAAAAXy8suLhIPHCsrKxwPEi4uLhPsxMTE7BMug4IUISEhHQ8C9Ofj4+MAOTk5JQLhy8vLgwCAAQAIAFIAAACBBP77+/v+gh4CBQUFDRYAzejp9gDHpOcauquZmZm25gAbSmdnZ0obg4EE+voABwaCHgYHAAkbIDX94/AAV1cl5ht1YCYA5b+pqam/5QAaQVeDAIABAAgANQAAABm9vfz7CBUVFTNPKbvtICAg/ufCrKyswuf+IIODBf706vLv4oEN0tIAKCgoHADq07aqqqqDgAEACAAFAAAAgAAJg4WAAQAIAAQAAAEAAQAFAPYAgAEACABlAAAAMQfx5ePeQz0pEgnqwa6urrfYA+/g3t7e7wQLEB8qK8nQ5Pn9ETdPT08pG/wFFR8fHxgNg4EVChQTNkZVWFhYRCEI+d/TxcbS4OTw/IIV+uvdurOsqKiotdTtBhwiLjAsJiEXCYSAAQAIAAUAAACAAASDhYABAAgABQAAAIWAAPaDgAEACACoAAAAP/749/f56/L26/Hx8e70+gME/fT09+ni495DPSkSCerBrq6ut9gD7+De3t7vBAsQHyorydDk+f0RN09PTykb/AURFR8fHxoSDQwICA8NGysrKxX/gyv19e7pBAoJCQn3/Pv4+Pj7+fX6/AEDDBQTNkZVWFhYRCEI+d/TxcbS4OTw/IIi+uvdurOsqKiotdTtBhwiLjAsJiEZCwIA///+/v4DAvfn6fWDAIABAAgABQAAAIAAAYOFgAEACABfAAAALgIA+vXxPTod+t2xsbHD3+zv8/XZtNEJJCo7SUlJ19fXytj//AYMDEI6JRYWFg8Gg4EZBAkMKDpSUlIzGwjv5eXl5eYWwLiurq6uv9uBAgHw84IJCguxGgkACxgWCYSAAQAIAAoAAAQDAQICAgPH8g45A5oAmgCAAQAIAAUAAACFgAD2g4ABAAgAVQAAACjHx/LyDg45OQwMCAgPDRsrKysV//749/f56/L26/Hx8e70+gME/fT094OAAZqagQGamoEe///+/v4DAvfn6fX19e7pBAoJCQn3/Pv4+Pj7+fX6/ISAAQAIAAUAAACAAAGDhYABAAgAKAAAABIC6t7e3lBQUCMD7sewsLAiIiIXg4EBBxaBBwM0WlpaSSMDgQEWB4QAgAEACAAFAAAAgAAEg4WAAQAIAAUAAACFgAD3g4ABAAgABQAAAIWAAAqDgAEACAAFAAAAgAD8g4WAAQAIAAUAAACAAASDhYABAAgABQAAAIWAACGDgAEACABYAAAAKQzny8fHx8K+q6e2zN7e3lBQUCMD7sewsLAiIiIhHyYiIiIbGh8pLi4zFIMN+PgEEA0KAPr/FCg0LBmBBwM0WlpaSSMDgQ8WDwkHCxQWGiAgICIi+fv4gwCAAQAIAGEAAAAu/PPi2NjY4vP8BhspKSkbBv/89vb2/P8CCAgIAgLq3t7eUFBQIwPux7CwsCIiIheDG+fn8gURHS84ODgvHREF8ucYGBQRDwoKCg8RFBiBAQcWgQcDNFpaWkkjA4EBFgeEgAEACAAPAAAAB8TuWQUFsBI9g4IBaWmGgAEACAAjAAAAD+X6QwsL1Sf09LsFG8///zGDgkEAhwCHgUEAhwCHgwGenoSAAQAIAAUAAACAAAKDhYABAAgABAAAAQABAP4A9wCAAQAIAAQAAAEAAQD+AAoAgAEACAAFAAAAgAD6g4WAAQAIAB8AAAAK7s3sWgEBqRU0E6aBAFuDgAD+gQFhYYEA/oEBnp6EgAEACAAXAAAAA83N4laBA60eMDCDgAD6gQFgYIEA+oSAAQAIAAUAAACAAAKDhYABAAgABAAAAQABAP4A9wCAAQAIAAQAAAEAAQD+AAoAgAEACAAFAAAAgAD6g4WAAQAIABQAAAYFAQECAgECAPpA/28B+glBAJgACQVEnAC8ZACAAQAIAAUAAACAAAGDhYABAAgABAAAAQABAP0A9gCAAQAIAAUAAACAAP2DhYABAAgAUwAAACfa3+Dg4N7MzMzd/BcsLd3i8gYSMjIyMjEyx8m9vBQD4szMzBJRUVE0g4EVCgwMCwYK8cfHx9nmCf4FBQUI/vcI/IEN6uMAPDwxGgnV2vgXKDyDgAEACAAFAAAAgAAGg4WAAQAIAAUAAACAAAaDhYABAAgABQAAAIAABoOFgAEACAAFAAAAgAAGg4WAAQAIAAUAAACAAAeDhYABAAgABQAAAIAABYOFgAEACAB5AAAAOhDrz8vLy9THyb282t/g4ODezMzM3fwXLC3d4vIGEjIyMjIxMiYmJiYmISUvMjI3GBQD4szMzBJRUVE0gwn4+AUPCg0HAOrjgi0KDAwLBgrxx8fH2eYJ/gUFBQj+9wj8AAMLExggICAiIvj7+Dw8MRoJ1dr4Fyg8g4ABAAgABQAAAIAABoOFgAEACAAFAAAAgAAGg4WAAQAIAIsAAAA/CAgGBgYNA+3t7fsTJDEzBgQLGhELEQ4C/Pr8/wMDJCcZA+/s49vRBPnh7/f7+foAHhL87e3tEzlGRkYzI76+wwPZ8QAgg4EmDBIYGA8LCvHBwcHc5Qv/BQUFHi8cBQUFBgHv29v+Jzo/Pz83KRX+ghfr3fIAQUE2HAfb3vINGypBCwsG4sHBwd6DgAEACABFAAAAISM4R0hIQ9jYR0dGRDYjIBkVFRUZIPPJoqKiyfMcPz8/Lw6DgQL16+2DGBkXDQUFBQQFBgcEAE9PKgbit7e36gMRNE+DgAEACABAAAAAHv3q4OHh4e339QEjJ9DZ9gUbPlJSUj8fCPTUzh8XBfqDgRv/AwkQDAUFBQHszcS3t7fM7wYZOk5OTjstEAH8hACAAQAIAAUAAACAAAWDhYABAAgABQAAAIAABYOFgAEACACCAAAAPvv19PT26O/z6O7u7uvx9wAB+vHx9Obf4eHh7ff1ASMn0Nn2BRs+UlJSPx8I9NTOHxkMBQoJBQUMChgoKCgS/IMT9fXu6QQKCQkJ9/z7+Pj4+/n1+vyBKAEECRAMBQUFAezNxLe3t8zvBhk6Tk5OOy0QA/4CAf///v7+AwL35+n1gwCAAQAIAAUAAACAAAWDhYABAAgASQAAACLd4ebr6+vn4d2/ubm5KCgoKyi8wLe/DvLRwcHB5Q43Xl5eN4OBCQQIBgUEBQUFFxiBAgz27oEP6+oAT080EQPst7e34gYqT4OAAQAIAFsAAAAry8/V2dnZ0suxp6amEBAQEg6nsKuy99Gurq7R9x5GRkYe9vn6DhAeISEhGx2DgQgECAYFBQUFHyiBAgz27oEZ4uYAT08YA+y3t7fiBipP7vP7/v7+7ujVz+6DgAEACABZAAAAKt3h5uvr6+fh3b+5ubna2rm5KCgeHigoKCsovMC3vw7y0cHBweUON15eXjeDgQ0ECAYFBAUFBRcY7+8XF4EGFxfv7wz27oEP6+oAT080EQPst7e34gYqT4OAAQAIAG8AAAA1//Ts7fDw8O3r7fPgt5+kqbS4uKKbnsz19e7f3e4E+PkfHx8XCwEL+9Gzs7PS/AwbRGNjY0Mag4EzAgL/+vLu8fX19QkxTQra0c7l3dS4vhQOGh8xGhAfDv349/wASUk7Gv3eu6ysrLrc+xg7SYOAAQAIAEkAAAAiCgb05OTk8QEFCRQgJyVNU0QqDgbx3dgjF/5KyMXO6woZNkmDgSD5+gcGBQUFBQYA7tnZ/y1FTk5OPDEUAwAMDAXevLy8yu2DgAEACAAFAAAAgAAHg4WAAQAIAAUAAACAAAeDhYABAAgABQAAAIAAB4OFgAEACAAFAAAAgAAHg4WAAQAIAAUAAACAAAeDhYABAAgABQAAAIAACIOFgAEACAAFAAAAgAAGg4WAAQAIAHAAAAA1At3Bvb29tsHW5OTk8QEFCRQgJyVNU0QqDgbx3dgjIBsaIBgYGBEQFR8kJCkKSsjFzusKGTZJgzX4+AQQDQX8EBoRBwYFBQUFBgDu2dn/LUVOTk48MRQODAsOFhYaICAgIiL5+/gMDAXevLy8yu2DAIABAAgALQAAABTIyObmyMjI4QwlNjZJPjc3Nx4eNzeDgAGwsIEK3uv6AgICsbGxvM2BAbCwhIABAAgAYgAAAC4ECAj87Tc7GgXszb6+vra81uTp5ubm6eHQvLq+xCwsLCIQDfPTxcXF5w01WVlZNYMb7e3p6OsXKDc3NyoVCf//EBAQDwoGDw0FBQUWF4EQLg3y7V9fQhsL9Le3t+0QMl+DAIABAAgABQAAAIAADoOFgAEACACCAAAAPvbZxMTE0PIVLRsTFxcXMDkECAj87Tc7GgXszb6+vra81uTp5ubm6eHQvLq+xCwsLCIQDfPTxcXF5w01WVlZNYMr9/oIDg0QFBP9/v4BCgcKEO3t6ejrFyg3NzcqFQn//xAQEA8KBg8NBQUFFheBEC4N8u1fX0IbC/S3t7ftEDJfgwCAAQAIAAUAAACAAA6DhYABAAgALQAAABXa2klJSEU/NzUvKysrvLy81voUSUlJg4IIDQwIBQUFCAP1gQbj2be3t9PrhIABAAgAPwAAAB3a2uTk2tpJSSgoSUlIRT83NS8rKyu8vLzW+hRJSUmDgAPv7xcXgQwXF+/vDQwIBQUFCAP1gQbj2be3t9PrhIABAAgABQAAAIAAF4OFgAEACAAMAAAFBAECAgICBPDe8U07BFCwAFAAAIABAAgABQAAAIAAF4OFgAEACAAFAAAAgAAXg4WAAQAIAAUAAACAABeDhYABAAgABQAAAIAAF4OFgAEACAAFAAAAgAAYg4WAAQAIAAUAAACAABaDhYABAAgAWAAAACnw8N7e8fFNTTs7Ly8vLy8qLjg7O0AhGfTY1NTU184XAujo6AIXK0ZGRiuDgANQULCwgSJQUAADCxMYICAgIiL4+/j4+AUPCgsGAOnpAxgsR0dHLBgD6YMAgAEACABeAAAALQcH4tTKysrKwcE5OTkgBQGvr6amHh7o07m5udPo/BcXF/wD7tTU1O4DFzIyMheDgAdRUVFHLBSwsIECAv/+ggGwsIIX6ekDGCxHR0csGAPp6ekDGCxHR0csGAPpgwCAAQAIAAUAAACAABiDhYABAAgAIgAAAA/g4Pfp39/f3/HxTk5ONRoWg4AHUVFRRywUsLCBAgL//oUAgAEACAAFAAAAgAAYg4WAAQAIABkAAAALrq4dHdNRWD/BDh0dg4IAOoEA+YEBrbeEgAEACAAFAAAAgAD/g4WAAQAIACAAAAAOEfnYyMjIyMg3Nzc+Rzg4g4EEBBkysLCBBGBTUVFRhACAAQAIAAUAAACAABWDhYABAAgABAAAAQABADIA/gCAAQAIAAUAAACAABWDhYABAAgAMAAAABYR+djIyMjR0cjIyMg3N1dXNzc3Pkc4OIOBCAQZMufzPjKwsIEIZ20iHGBTUVFRhACAAQAIAEcAAAAi4+MnKzAxKCEbHSMiGhccHBzNzc3d9QgnJyfY2Njn/hEyMjKDggwKDAUFBQ0REQUFBQUBgQbh07e3t8/mgQbg0be3t8/mhIABAAgALQAAABXY2ENJSEY+NTMtKSkpurq61PgSR0dHg4IIEA4JBQUFCAL1gQbk2re3t9PrhIABAAgABQAAAIAAAYOFgAEACAAFAAAAgAABg4WAAQAIAAUAAACAAAiDhYABAAgABQAAAIAAAYOFgAEACABLAAAAgCL37urr6+vq7vcACREVFRUVFREJAO3CpKSkwewAFT9cXFw+FIOBIQIEBQQDAwQFBQUEAwMEBQQCAE5OQB8E6ce3t7fH6QQfQE6DgAEACAACAAAAhYUAgAEACAACAAAAhYUAgAEACAACAAAAhYUAgAEACAAFAAAAgAABg4WAAQAIAAUAAACAAASDhYABAAgAZwAAAIAwBBEUFPTy7+vr6+ru9wD88e7qChATFRUVFREJAO3CpKSktcVNPhU8tcTsABU/XFxcS4OBL/To4/v9/gEEAwMEBQUFER0dBQgIBQQFBAIATk5AHwTnvK0tPk5X1sa3t7fH6QQgSYOAAQAIAAIAAACFhQCAAQAIAHwAAAAX5/T9+voNEgsKDg4OCgoPB/v9/vbx8fb7gSIaHwnu6d/YzgH13RYK8N7e3vAKFiI7TExMOiEYu7vA0eXxE4OBAt7H5oI0AwUEAwQFBQUeOyMFBQUGAO/b2wk0Pz8/NScT/gBJSTwdBOrLvLy8y+oEHTxJCwsG4sHBwd6DAIABAAgAQwAAACDY2EJFRUM2IyAZFRUVGSAjOEdIR0fzyaKiosnzHD8/PxyDggoWFg0FBQUCAAECAYIP9/DyAE9PJAHdt7e38AUcT4OAAQAIAEMAAAAg2NhHR0ZENiMgGRUVFRkgIzhHSEdH88mioqLJ8xw/Pz8cg4IKGRYNBQUFAf4AAQGCD/fv8ABPTyQA3La2tu0FG0+DgAEACABZAAAAGNTKr5ubm6CpxtLi6+vr4tTLq52boAoKCvyBAPyCDOf/z6Ojo9D/L15eXi+DgQQGGzLv8IIJAQIBAAIFBQUWGIEVY2pgYGBeXAQGAE9PGwPrt7e34gMlT4OAAQAIACcAAAASzc0oMj43KjA0PT06JhkaKzw8PIOCDiIcCQkJCAaipqqqqrPH2oSAAQAIAAUAAACAAA+DhYABAAgABQAAAIAAD4OFgAEACABYAAAAKv3r3NfUKSQS/tnExMTw/PHm2NjY6v8GCiUv2Nv5BhVAQEAeEiIzLCwsHgiDgScEBQIhMkVFRSQR7drV1NPm6PYBBQUFBfPUy8HBwdbuAxgcHBwWGAwChACAAQAIAAUAAACAAAiDhYABAAgABQAAAIAACIOFgAEACACaAAAAP/v19PT26O/z6O7u7uvx9wAB+vHx9OHY1CkkEv7ZxMTE8Pzx5tjY2Or/BgolL9jb+QYVQEBAHhIiMywsLCITCgkJBQUMChgoKCgS/IM/9fXu6QQKCQkJ9/z7+Pj4+/n1+vwAAQYCITJFRUUkEe3a1dTT5uj2AQUFBQXz1MvBwcHW7gMYHBwcFhgOBAIB/wn//v7+AwL35+n1gwCAAQAIAAUAAACAAP6DhYABAAgAYwAAAA8rIg4F6e4ACujDtbW1y+4BgR3oycnJ8gMFIz8/P9DQ0OH3/QEQHBwcK1ZBJycnJymDGwEBBAVjX1lZWUIeB/fXv7+/Dw3o3My3t7e+1/GBAvYPEIILAfXg5Ovx8fcD//4Bg4ABAAgAKAAAABQO9tjKysrW1srKOTkqKjk5OUFEKiqDgQQEDxuwsIUGsLBDTFFRUYQAgAEACAAEAAABAAEAVgAMAIABAAgAaAAAACfy7Ovr7d/m6t/l5eXi6O73+PHo6OvbysrK1tbKyjk5Kio5OTlBRCoqgQn8/AMBDx8fHwnzgxn19e7pBAoJCQn3/Pv4+Pj7+fX6/AIEEBuwsIUGsLBDTFFRUYEK///+/v4DAvfn6fWDAIABAAgABQAAAIAA9YOFgAEACAAqAAAAFNPN09PTQkJCMBcK8729vSwsxLu2x4OBAQcggQc4QUtOTk4mEYMB9vKEAIABAAgABQAAAIAAAYOFgAEACAAFAAAAgAABg4WAAQAIAAUAAACAAAGDhYABAAgABQAAAIAAAoOFgAEACAAFAAAAgAAFg4WAAQAIAAIAAACFhQCAAQAIAFEAAAAn083T09NCQkIwFwrzvb29LCwgICAgIBsfKSwsMRIK5cnFxcXPwru2x4OBAQcggQc4QUtOTk4mEYIVAwsTGCAgICIi+Pv4+PgFDwoPCQD28oSAAQAIAAUAAACAAAGDhYABAAgADwAAAAe361r//6cVSIOCAV9fhoABAAgAHwAAAA/N6jgQENkq7+/JFjLK//81g4IBSUmBAUdHgwGjo4SAAQAIAAUAAACAAAKDhYABAAgABQAAAIAAAoOFgAEACAAFAAAAgAACg4WAAQAIAAUAAACAAAODhYABAAgAHwAAAA3azeNXAwOpHDMlswEBToOAAAeBAWJigQAIgQGuroSAAQAIACgAAAAS5+fi7fTs0Otg//+eFVlQQC0R/oOABUhISEEz+YEBfHyBA00zFAWFAIABAAgAAgAAAIWFAIABAAgAAgAAAIWFAIABAAgAAgAAAIWFAIABAAgABQAAAIAAAYOFgAEACAAOAAAGBQEBAgIBAgXcheQfeCUFUq4ArlIAgAEACAACAAAAhYUAgAEACAACAAAAhYUAgAEACAACAAAAhYUAgAEACABQAAAAJvnv5+fn3uHh3eoBESUp8PYGECIlJSUlIBzp7PT/FPfk5OQKKysrIIOBDQ0KDw8SDfHZ2dnq9A0OggQDCCQtEoEM9f8AKioD7/D4AhUkKoMAgAEACAA1AAAAGwL57ejo6O35AgwVGBgYFQwC7tXV1e4CFisrKxaDhAECAYIBAQKDCzAwEwDu0dHR7gATMIOAAQAIABEAAAAH/MM8BGuN/PyDgwNZWY6Og4ABAAgAWgAAACsEBNbW3+zy8vL0+P0AAwgMDg4OFCEqKvz8z8/Hv7y8vM/uABIxREREQTkxMYOACUlJGhsUBwIB//+CCf//AQIHFBsaSUmBEFVKMhcK8Mq0tLTK8AoXMkpVhACAAQAIAD0AAAAesbEgICAQ+fDcra2tHR0dJi03N+XizMG+wtv2CCQgIIOCBzhBS05OTiYRgQRmWVZWVoID8uHd7IIB8ueEgAEACAAcAAANDAACAgIBAQEBAwEBAgIM2cQxKioqLjED/enZLIAGsgCyMj8/P4EC/vayAIABAAgAFAAACQgABAEDAQEBAQOAB8HBABEuPz8RgAc/UH5+bVA/AACAAQAIACQAAAAP3efWwMDA0e7/GT4+Pj0+HoMP6QQKLT9QbX5+flk/OTEy6YMAgAEACAAFAAAAhYAAlYOAAQAIAAQAAAEAAQD/AJUAgAEACABIAAAAIwj64+Pj+ggXLi4uF//x2tra8f8OJSUlDvfp0tLS6fcGHR0dBoOBCBYlNEtLSzQlFoIIFiU0S0tLNCUWgggWJTRLS0s0JRaEAIABAAgAJwAAABHWwcE9PSj/58bGxuf/Fzg4OBeDAVL+gQH+UoEIITlQcnJyUDkhhIABAAgAJwAAABHBwdYoPT3/58bGxuf/Fzg4OBeDgAkCrq4CAI6Or8feggPex6+Og4ABAAgAXgAAAC2urq64x8zOzMzM5/YMFQoCpLLM5vYML0FBQTo3OzAkHh4e6dGwsLDR6f8hISH/gxJPNUBPT0AuBe/Nrq6ux+HjEQ8Hggv89vLv8wYYHyAlMk+BCCE5UXFxcVE5IYQAgAEACABeAAAALQrz0b+/v8XKxdDc4uLiUlJSRzk0MjQ0NBkK9Ov1/lxNNBoXAN/f3wAXL1BQUC+DgSQECg4QDfvo4eDazrGxy8CxssDS+xEzUlJSOR4d7/H5AI+Pr8feggPex6+PgwCAAQAIAAQAAACEAMqDAIABAAgABAAAAIQAyoMAgAEACAAEAAAAhADKgwCAAQAIACEAAAANDujZ9ATn5xgY+gsnFu+EDu0I8e0YIAQEIBjt8Qfs14OAAQAIAEQAAAAfzMHv+Mq86vPGuyEs2M0zPhEINUMWDTlE3tMnMjDdzyKDgAfKyiws1NQ2NoEBNjaBBzY21NQsLMrKgQbKygAsLNTUgwCAAQAIAAcAAAAD7KAUYIOHgAEACAAHAAAAAxSg7GCDh4ABAAgABgAAAgEBAgHjHQEq1oABAAgABgAAAgEBAgH2CgEt2YABAAgABgAAAgEBAgH2CgEt2YABAAgABwAAAIcArIEArIOAAQAIAB8AAAANpK26urqtpBQbIyMjGxSDgAQHAQD/+oEE+voABgaEgAEACAAfAAAADezl3d3d5excUkZGRlJcg4AEBgYA+vqBBPr/AAEHhIABAAgATQAAACUXAt3FxcXFxsrKxsXFxcXdAhchITsyLCwsLCANDSAsLCwsMjshIYOBDREzS9LT09MtLS0utc7vggepqamppQkLCIEH+PT3W1dXV1eEgAEACABNAAAAJd/fxc7U1NTU4PPz4NTU1NTOxd/f6f0jOzs7Ozo2Njo7Ozs7I/3pg4AHV1dXV1v39PiBBwgLCaWpqampgg3vzrUuLS0t09PT0kszEYWAAQAIAAoAAAQDAQICAgPQFCMUgAKsVACAAQAIAAkAAAQDAQICAgPs3ewwAVSsgQCAAQAIAAIAAACEhACAAQAIAEQAAAAfsbiqlZWVpb/P5wkJCQgJ7RIZC/b29gYgMEhqamppak6DH+b6AB8vP1lpaWlHLykjJObn+wEgMEBaampqSDAqJCXngwCAAQAIAE4AAAAfMRn39/f39xNPSFdra2tbQNC4lpaWlpay7uf2CgoK+t+DQf99/30En7e8w8KBBezmx7enjUL/ff98/3wMnra7wsH//+vlxramjED/fIMAgAEACABLAAAAHxIZC/b29gYgMEhqamppak6xuKqVlZWlv8/nCQkJCAntg0D/fg2SmLfH1/EBAQHfx8G7vEH/fv99BZGXtsbW8IIE3sbAurtA/32DgAEACAAqAAAADwHnwsLCwsLiIxkqQEBALxKDQf9f/18LhJ6krKv09NnSsJ6MQf9w/1+DAIABAAgAJwAAAA/d59bAwMDR7v8ZPj4+PT4eg0D/awWGjK/B0u+CBNvBu7O0QP9rg4ABAAgADwAAAAvc3NxcW1ylpaUlJCWDj4ABAAgADwAAAAvb3NtbW1ukpaQkJCSDj4ABAAgACQAAAAXAwMBAP0CDiYABAAgACQAAAAXAwcBAQECDiYABAAgAFAAAAAepqxQW6uxVV4MA9IEB9PSBAPSDAIABAAgADAAAAAPJyzQ2gwD0gQD0gwCAAQAIAEoAAAAjFQfp19fb2uDfzbu7u9LSu7u73AsfJCRJOy8vLyQkLy8vNTgqg4ELAQJiZWtra2NgZa2tgQoNGiUnJyfS0tLi9IEFra31Bg4IhACAAQAIAEgAAAAi9uzd09PT3e32DBjx8SgoJCEhIigtLS0U9vHy9/v9KCgoGgODgQkKGiMtPEZGRjECgRMHERcXFhEMC/rjz9Da39zbCUEoC4QAgAEACACaAAAAP/vv7PP7+/v7/QEUGhEFBQX/+Pj48+7z+f7/+vT09Pf7/AAKDQ0NA/7w4+Dg4OXz/wISICAgEgMCBPn4+PwJ+ukK6env8/sCFicnJxODFvb2/gYF+vwECgoK/O3v/v/8+vr69vr/gjABAgIA/v7+/v7+KyQgICAI9wP/9O3t7erw/REZExMTFBT29jQ0HRfV0tLS0tz1DCA0gwCAAQAIAHwAAAA7EAkC/v7+6tXV1e7u7vkKEyItLi4uRFRK+f4HDVhLKiMKquHa9B3zvbMCIWJiYkkpJBDm5uYFECU9PT0mgx/+/gQIBvPt9/sCBv4KFhwcHAj2+QME9uo4MykfHw/0+YEZx9r+VFQpBbnH5g4rSVQkJRT339LS0uj5DR+DAIABAAgAEgAACAcBAQIEAgICAgfP1dzcHRHk/AEECIED7ADsAIABAAgAjwAAAD/57eHZKScbA+/JycnO3/Dg38zMzLGTtMjIyNPuBxMfJ9fY5f0RNzc3MiIQICA0NDRQbUw4ODgtETAGxsbG4f3QBfk6OjofA4OBGAsQNz5RUVE3IhkK++/w7P0JBf367uLX4vSCJ/bwycKvr6/J3uj1BhEQFAP3+wQGER4pHwwAPzIK7tzDscHO9xIlPk+DgAEACAB5AAAAgAIDAwKCBgIDAwD+/f6CLf79/vry9vb29/nuBg/v9f0DEiEhIRME+/PtCwTy7QD27+/v7+/2AAoREREREQqDgwz////+/v7+/v7+////hAIFAPuCIgzw4NnT09PvAAwtLS0mFgf39gAMDAT9/wD68vLy+gD//QQMg4ABAAgAdAAAAIACAwMCggYCAwMA/v3+gir+/f4A9u/v7+/v9gAKEREREREK4eH6+gQNDQ0RDA3q6wEBAfjr6enp8v4Bg4MM/////v7+/v7+/v///4IPDAwE/f8A+vLy8voA//0EDIMFCAn+Av//gQv+/gAfHx8I/e7d3d2DAIABAAgALwAAABfu7vj4CwsVFe3tMPz8ygkJ5eXlC+sTExODgAHV1YEB1dWDAUNDgwHW1oEB1taEgAEACAAiAAAQDwABAQIDAgMCAgECAQECAQGADvPf0/MNLSEA/vr6/gIGBg+jo6/S/v7Sr9jY0s7MzM7SgAEACAAHAAAAA8zMNDSDh4ABAAgACwAAAAfW1ioq1tYqKoOLgAEACAANAAAGBQECAgICAgXM0swzLjMAuIIBuAAAgAEACAAVAAAKCQECAgICAgICAgIJydTJ1Mk2LDYsNgICQMKCA8JAAgAAgAEACABJAAAAIgLz7fP7+/sBBwgCAwUFBTAwJhEC9OLaFhYKMM/P2PECESYwg4EgDBQJ9AURDwoKCggFAgItNkFBQTs1GxAAPj7f1cnJydXfg4ABAAgAUAAAACX9/ena2NjY3Oz9/SYmHyErL9nkDCYmDuPYKCEcJib9/QssRkZGGYOACP8A/gEHCQoJCIEZBQEA+OjLvrO4UFI+Kw0BBgIAUre1wuUDKFaDAIABAAgAbAAAADMO8/X5////+fXzDhAPBwD58PDyDQsHAQEBBwsN8vDw+QAHDxAA68m1tbXJ6wAVN0tLSzcVgzPxDhANBf/58O7wDQoGAQEBBgoN8O7w+f8FDRAO8fT4/f39+PRKSjYU/+rItLS0yOr/FDZKgwCAAQAIAHgAAAA5+fno4eHdQDcO+fn57uHc3Nzt/Pn5HBwgKC4uztkLHBwGDBkhISEdGhwcHOyysrLVCxz5+RhLS0sjCIOAEgEEDRQTN1BeW8nIydHb3u/6+/6BI//99ejduqugpCIoKiolIBcLBQIAWFotB/DVyMQoqKnL8AUgJYMAgAEACABgAAAALQb99fDu7P7o6erq6/3w9foAAwwMDrO31P8sYGEE8llaWQDuYF9KIwXXubMOCwmDLQEBBgHx8RgYDvnt7RQUEQb9/f32+trKoaGh2hQU7e0DGBjx8Qs+XV1dMCcGCwGDAIABAAgAgQAAAD7zydzfz7+/v8fPzMy4uru7u83r+/8hMtzf6Pv9GTIyMi8y8/NEOjU1NR0XIjxKRDsoEhomGTpAIv4B+vX7BvmDCv5UXVtLMycY79nZgRPz9vP2+/7+/v33zLqrq6utw+Hq+4ET2dn0FyM0NzQ8QUFBTFdXV15gCQiCBP/9/f0Dg4ABAAgAMwAAABfJye7uycnu7sDPXwMDqTE/FRU1NRUVNTWDgAft7RQU7e0UFIEBeHiBBxQU7e0UFO3thIABAAgABAAAAIQAwoMAgAEACAAHAAAAA/G1EEyDh4ABAAgADgAABgUBAgICAgIF1PbUKworBdQsACzUAIABAAgABgAAAgEBAgH3CQEq0oABAAgAHAAAAAsm58TkIgHfHT0a2wGDC+YjAN0cOxzdACPmwoMAgAEACAAmAAASEQECAgECAQIBAgECAQIBAgECARH2Cvfo6PcKGhoK9+jo9woaGgoRLNTT4/cHB/fj0/gKHywsHwr4gAEACAAKAAAEAwECAgID9wn3CQMy2izUgAEACAArAAAAE86v9/fttPf38M0zVgkJGlMJCRUzg4AH1NQsLNraMjKBBzIy2tosLNTUhIABAAgAEgAAAAbv76Tv7xERgwbpRAO/Gx7ngwCAAQAIABIAAAAGEe/vERFdEYMG6eceG78DRIMAgAEACAAZAAAACt/fs9/fISHf3yEhgwkrdybRHE/5AFRUhIABAAgAGQAAAAoh398hIU0h398hIYMJK/lPHNEmdwBUVISAAQAIABIAAAgHAQICAgICAgIH1evVLBUs6xUHDEwATAxbVACAAQAIAGQAAAAvTREQDxYTAu3Z08G4tPDx8uvu/xQpLkBJTREQDxYTAu3Z08G4tPDx8uvu/xQpLkBJgy/F5PsRERETFBYWFgkO79jCwsLAv729vcrc+xIoKCgqKy0tLSAlBu/Z2dnX1tTU1OGDAIABAAgACAAAAwIBAgICvfMNAtotxgCAAQAIADQAAAAXThAOCxcWBe/a1ce9tfP2+ezt/hQpLjxGgxfE4wUmJiYoKCoqKig6G/nY2NjW1tTU1NeDAIABAAgADQAAAAbg3yEgwQE/g4QAiYSAAQAIAGoAAAABGhaCCBYaGg0A8+fm6oIi6ubn8wANGh8ZDPXd9QwZHzBISEgw4dG4uLjR4ef0DCMM9OeDM/Ly+AAIDg4OISshDg4OCAD48vLy4NXg8jIyMR0A5M/Ozs7uABIyMjISAO7Ozs7P5AAdMTKDAIABAAgAVAAAACf3487Oztbd5Ojo6Ojp39TU1tfm/QkeMjIyKyMdGBgYGBchLCwqKRkDgyf+/hUgKjMzMysmJiIoLygZqMLp/v7+59zSycnJ0dbX2tTN1ONUOhP+gwCAAQAIAAkAAAQDAQICAgPoGMg5gQHDAACAAQAIABsAAAAL0tLD0tIuLkg+SC4ug4ACUQGvgQSvrwFRUYSAAQAIABMAAAAIzu3DwyXuthUwg4ABpqaBAH6GgAEACABkAAAALfHj2dzi4uLXzs/c5NfV2ur28+Pju67M+R4eHjE6Hv30zq6urs70/QUrS0tLKwWDgRABAPTlzsDFzs7O0uHSv7azs4EPIkpjVCxBOhwAT09GGua1ikL/f/9//38FirXmGkZPgwCAAQAIAEAAACAfAQUBAgUBAgECAwIBAgEBAQIEAQECAQECAQECAQIDAgEf+Pf49PX04sbGCiYm4tseJQsLCwsICQkJCQj22toeOjoLAf79/QEBLxTrz/8Ugw8B//39/f3/AAEBLxTrz/8UgAEACABiAAAwLwEBAQIBAwECAQEDAQQBAwECAQMBAwEDAQMBBAEDAQIBAwEDAQMBAwEEAQMBAQEBAS8VFhcXFhMSERESAecUKEEoFRYXFhMSERIB5xQoQSj9/v/++/r5+unP/BApEBcX+fkvAgMBAP8A/wEBAyoR2NgRKgAB/v3+/QABKA/W1g8oAAH+/f79AAEoD9bWDyjpHxPdgAEACAAUAAAAB//E/zz/5P8bgwfh/Rb9Uv2m/YMAgAEACAASAAAIBwEDAwMDAwMDBwz3KD3Yw/QJB+ITKPfiEyj3gAEACAAEAAAAhAAKgwCAAQAIAA4AAAYFAAICAgICgATe3gAiIgXk+RMoE/mAAQAIAA4AAAYFAAICAgICgATe3gAiIgXsARswGwGAAQAIABIAAAgHAAEBAQIBAwGABuvR0esALy8H6ekDGEdHGAOAAQAIABwAAAAL29rV2NjY6/kABwongwsG/PsEBREjIyMiHwaDAIABAAgAHAAAAAvV09TZ2dnn+QAOESeDC/b/Ag4QGy8vLygh9oMAgAEACAAcAAAAC9j1+P8GFCcnJyolJIMLBh8iIyMjEQUE+/wGgwCAAQAIABwAAAAL2e/yAQcYJycnLC0rgwv2ISgvLy8bEA4C//aDAIABAAgANAAAABetyczT2ef////++foCHiEoLjxUVFRRTk+DFwYfIiQkJBAEA/r7BgYfIiQkJBAEA/n7BoMAgAEACAAEAAAAAP+DhACAAQAIABsAAAAL4+bn+wASHR0dGRYYgwLw+fyCBevj3dTR8IOAAQAIABwAAAALsfr5/v8CBwdPAAIBgwv+NjM1NTUzNv7+//6DAIABAAgABAAAAIQA94MAgAEACAAYAAAACf/6+rEBAgBPBwaDCf7+/DU1NDU1/P6DAIABAAgABAAAAIQA9oMAgAEACAAgAAAADf/t390NDAP/+/bzIyERgw319QMSEhkjIyMZEhID9YMAgAEACAAEAAAAhAD5gwCAAQAIABwAAA0MAQECAQQBAQECAwMDAwz25dvbCholJRr9+AMIDNzlAAskGwsA5Qb9+gIAgAEACAAmAAASEQEBAwECAQEDAQECAQIBAgECARH04dbh/wodKh0K/ff3/QMJCQMRytUBFB4eFOnVyv338evr8ff9gAEACAA8AAAAGw0A/gMDBAUDzs/e+QEEAgMBAwYLEhYkMjIyHAmDG/j49/X19fr8/AchISEgICAgGhkbHx8fDgj38/iDAIABAAgABAAAAIQA9YMAgAEACAAGAAACAQECAegZARHVgAEACAAEAAAAhAAhgwCAAQAIACQAAAAP5Mze5uLi4snAAyE1NTUpB4MP7QMBAv72+fbwCQf38vTw64MAgAEACABGAAAAIO3n5ubo2uHl2uDg4N3j6fLz7OPj6ff79/f+/AoaGhoE7oMg9fXu6QQKCQkJ9/z7+Pj4+/n1+vwF+v///v7+AwL35+n1gwCAAQAIADAAAAAVEu3Rzc3N1dbLNCgoKCgoIycxNDQ5GoMH+PgFDwoODQeBCwMLExggICAiIvj7+IMAgAEACAAwAAAAFRLt0c3NzdLRNDw2KSkpIyEmLzQ0ORqDFfj4BQ8KCAP+AAcQFBQbICAgISL4+/iDAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgABgAAAgEBAgHtFAHdqIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAAgAAAISEAIABAAgAUgAAAIAl797a2tre7wASIiYmJiISAO/QxSw6SUlJNBM+1ce3t7fL5f0AEDKDJgEBCAkA9/j9/f349wAJCAFJSTwtkZ7UAR8+SdJuXygB0Kuqtra2woMAgAEACAAUAAAJCAECAgEBAQECAgjqyezm2tXbNgoIVZ////r6AFUAAIABAAgAOAAAABrX18q/vLy82fYRMTTY5Pv9CB0pKSk9V2NbExODgAxVPR8F8t23t7fT6xAOggj89fH/HDhMUVGEAIABAAgAYwAAADH4DADdzSwuJf3ow62trbrHxsbGxtjSwMDAzN/qECcnydPw/vX8DyIvLy8qPSweHh4SAYOBH+zg7RogSUlJOR4M+N/T09MSEhITAOTWwre3t+TqEh0ShAn79e/89+//DAYChIABAAgAFAAACAcBAgECAgIBAwfT3thCIkJd0wbWDQA11gA1QP9vgAEACABKAAAAI/gI/N3OJikd7OPLubm5y+Tt+xEWuLkpKRYlIiEUFSEtLS0hCIOBEu/l7BQfSUlJOBgC6sy+vr7L0PqBC7CwKhcHBwcKCQICAYQAgAEACABYAAAAKvXizsbGxs/e7vYIFB2/xN7xDS08PkMsExIZICAgEPvz1q+vr9bxEjQ0NBGDgQUSHxQXEgiCHwT93M25ubnrNVk6EBAQEBETBP0ASEgjC+zHx8fvCyNIgwCAAQAIABEAAAAGwqjY2B8fQ4OAAa+vgQCvhIABAAgAfAAAAIA69uDS0tLHvdLt7e3v9wAKERISEi5DOS0tLR8KAOnKu7u7yukAFjREREQ0FgDu0L6+vtDuABIvQEBALxKDO////QcZFw0PD/np7vf9/f337un5Dw8NFhgH/f9XV0UoGAnq1NTU6gkYKERXKCgW+unYu6mpqbvY6foWKIMAgAEACABOAAAAJM2fnrDL5/r6+vz+/gAECAgIERwdNgLrxa+vr8rxARU7VVVVQR2DgCPa7wsYFwb2+Pr8/Pz59vQFEhEMAEhIMQz01bGhoaG12/UJLkiDAIABAAgAYAAAAIAs797a2tre7wASIiYmJiISAO/Nt7e3y+X9AAQcNklJSTQTAPPf39/zAA0hISENgy0BAQgJAPf4/f39+PcACQgBSUk+HwHQq6m1tbWpq9ABHz5J3t7y/wwgICAM//LegwCAAQAIAAcAAAAD2tsmJoOHgAEACABKAAAADNTU09Ps5eUGBuniJSyBFQsKAQEBCxQZJyYACxQYJDIyMkFRJCSDBfjNzQkHBIEA+IQVJyEYCv3z5OTk7PsGDAYGBgYDAxgnJ4QAgAEACAAkAAASEQECAQEBAgEBAQECAgECAgIBAxHJyOHa2vve1xoh7e7iGBQYHu4FzQkHBAD4gwf4GQce+AAe5IABAAgAdwAAADvj3B8m8vLz8+cdHRkZHR0j8/Pz/fvu5xAQCf724+Pj8/39+OXl5fP4BAwP5+33+woTFBQUFhsVFxcXEASDhCf4+BkHBx4e+PgAHh7k5Pb27fADChkZGQ7/7eLjCQsC9efe3t7r9gkIggv6+P36+Pv+AQAB/PaDgAEACAATAAAACNfX1dXi6ekSEoOABMPDBwf5hoABAAgAMgAAABfq6vDv6urq+QQGFRXp8gEFESIiIi5CFRWDgAwtJRT/8ujY2Njc8AoKggUD9vUZMDCEAIABAAgAVwAAACr///DmFxcSAfrd3d3wAgEBAO/e3t7v+AoUFubu+PkMFxYWFhkfGBoaGhIHg4EZ8/UOFCkpKRsJ9ODd4RMWFQTy49fX1+f2DgyCCvv7APr7AAMHAAIBhAAFAGYAAAISArwAAwAGAAkADAAPAAAzESERJSEDAxMDAREDJxMhZgGs/nEBc7rBtLQBg7YNu/6MArz9RBUBNP7oAS0BLP2lAl3+0hUBNQACACMAAAJVApwABwALAAAzEzMTIychBzchAyMj/Dv7OT3+uj5PASOPBAKc/WSoqNgBiQD//wAjAAACVQNHAiYAAQAAAAYBSDQA//8AIwAAAlUDRgImAAEAAAAHAVAAAACn//8AIwAAAlUDSgImAAEAAAAHAUwAAACj//8AIwAAAlUDOQImAAEAAAAHAUAAAACK//8AIwAAAlUDRwImAAEAAAAGAUbMAP//ACMAAAJVAx4CJgABAAAABwFWAAAAkwACACP/WAJVApwAGwAfAAAzEzMTDgIVFBYzMjY3FQYGIyImJjU0NjcnIQc3IQMjI/w7+xonFB8TCRIIDxUMFyYVKB87/ro+TwEjjwQCnP1kCBsgERcUBAMnBgMSIRcjMBCjqNgBiQADACMAAAJVA0MAFAAYACQAADMTJiY1NDY2MzIWFhUUBgcTIychBzchAyM3MjY1NCYjIgYVFBYj9hoiGisaGysZIRr1OT3+uj5PASOPBAIXICAXFiEhAowLLx4aKxoaKxodMAv9dKio2AGJTCEWFyAgFxYh//8AIwAAAlUDPQImAAEAAAAHAVQAAACpAAIABQAAAmwCnAAPABMAADMBIRUjFTMVIxEhFSE1Iwc3MxEjBQEMAVj+zc0BAf7HtkJVowQCnDD8MP7wMKio2AGXAAMAdAAAAjECnAASABwAJQAAMxEzMhYWFRQGBgceAhUUBgYjJzMyNjY1NCYjIzUzMjY1NCYjI3TjQlMoEichKTUZKFZHwLY2Qh5IULSxRDtES6ECnC5OLxw7MA0OMEAnMVQzMCdAJTdPMEYzNUwAAQA2//QCQQKoAB4AAAUiJiY1NDY2MzIWFwcmJiMiBgYVFBYWMzI2NjcXBgYBTlR+RkmCU0mBIDEWXUc8aEBAaDwxSTIQMiN+DFudZGSbWUtKEzFHQ4RhYYZFITcgE0xJAP//ADb/9AJBA0cCJgANAAAABgFISQD//wA2//QCQQNFAiYADQAAAAcBTgAVAKQAAQA2/08CQQKoAD4AAAUiJic3FhYzMjY1NCYjIgYjIiY3Ny4CNTQ2NjMyFhcHJiYjIgYGFRQWFjMyNjY3FwYGBwc2NjMyFhYVFAYGAUUTLREOFR0PHBQSFwsXCAsFBxtMcT9JglNJgSAxFl1HPGhAQGg8MUkyEDIieU8XChEEFRsOEiexDgoZCgccCg4QBhIKLAhdmF5km1lLShMxR0OEYWGGRSE3IBNJSgIkAgIQGQ4PJRr//wA2//QCQQM7AiYADQAAAAYBQxUAAAIATQAAAk8CnAAMABkAADMRMzIeAhUUDgIjJzMyPgI1NC4CIyNNpE2BXTMzXYFNbGQ5bFUzM1VsOWQCnCNPflxbgU8lMBc9cVtbcDsW//8ATQAAAk8DRQImABIAAAAHAU4ADACkAAIAFgAAAk8CnAAQACEAADMRIzUzETMyHgIVFA4CIyczMj4CNTQuAiMjFTMVI003N6RNgV0zM12BTWxkOWxVMzNVbDlkysoBRzABJSNPflxbgU8lMBc9cVtbcDsW9TAAAAIAFgAAAk8CnAAQACEAADMRIzUzETMyHgIVFA4CIyczMj4CNTQuAiMjFTMVI003N6RNgV0zM12BTWxkOWxVMzNVbDlkysoBRzABJSNPflxbgU8lMBc9cVtbcDsW9TAAAAEAiQAAAhkCnAALAAAzESEVIRUhFSERIRWJAYz+rAEV/usBWAKcMPww/vAwAP//AIkAAAIZA0cCJgAWAAAABgFISQD//wCJAAACGQNFAiYAFgAAAAcBTgAVAKT//wCJAAACGQNKAiYAFgAAAAcBTAAVAKP//wCJAAACGQM5AiYAFgAAAAcBQAAVAIr//wCJAAACGQM7AiYAFgAAAAYBQxUA//8AiQAAAhkDRwImABYAAAAGAUbhAP//AIkAAAIZAx4CJgAWAAAABwFWABUAkwABAIn/WAIZApwAIAAAMxEhFSEVIRUhESEVDgIVFBYzMjY3FQYGIyImJjU0NjeJAYz+rAEV/usBWBonFB8TCRIIDxUMFyYVIhwCnDD8MP7wMAgbIBEXFAQDJwYDEiEXIC4QAAEAiQAAAhkCnAAJAAAzESEVIRUhFSERiQGQ/qgBVP6sApww/DD+wAABACn/9AJCAqgAJgAABSImJjU0NjYzMhYXByYmIyIGBhUUFhYzMj4CNTUjNTMRIycOAgFDWH9DRn9XTX0fMhdeRD5nPDtmPxxGQCnI/SgNF0VMDFucY2ibV05NEztDRIRiXIZIEzBVQRow/rlhJzAWAP//ACn/9AJCA0YCJgAgAAAABwFQAAcAp///ACn/GAJCAqgCJgAgAAAABgFY+wD//wAp//QCQgM7AiYAIAAAAAYBQwcAAAEAQQAAAjcCnAALAAAzETMRIREzESMRIRFBOAGGODj+egKc/tUBK/1kAUH+vwACAAoAAAJuApwAEwAXAAAzESM1MzUzFSE1MxUzFSMRIxEhEREhNSFBNzc4AYY4Nzc4/noBhv56AgUuaWlpaS79+wFB/r8BcZQAAAEAjwAAAekCnAALAAAzNTMRIzUhFSMRMxWPkZEBWpGRKgJIKir9uCoAAAIACv/0AkYCnAARAB0AAAUiJjc3BhYWMzI2NjURMxEUBiU1MxEjNTMVIxEzFQGoS1sDOAMeNBwVLyI4WP4cUFDYUFAMZWsLQEsgFUA/AeT+G2JhDCoCSCoq/bgqAP//AI8AAAHpA0cCJgAmAAAABgFINAD//wCPAAAB6QNKAiYAJgAAAAcBTAAAAKP//wCPAAAB6QM5AiYAJgAAAAcBQAAAAIr//wCPAAAB6QM7AiYAJgAAAAYBQwAA//8AjwAAAekDRwImACYAAAAGAUbMAP//AI8AAAHpAx4CJgAmAAAABwFWAAAAkwABAI//WAHpApwAIAAAMzUzESM1IRUjETMVDgIVFBYzMjY3FQYGIyImJjU0NjePkZEBWpGRGicUHxMJEggPFQwXJhUiHCoCSCoq/bgqCBsgERcUBAMnBgMSIRcgLhAAAQBF//QB9AKcABMAAAUiJiY3NwYWFjMyPgI1ETMRFAYBIEFkNgI2BS5PKxg2MB44dwwsXUcLQEsgCx87LwHk/htiYf//AEX/9AJXA0cCJgAvAAAABwFIANAAAAABAGIAAAJYApwACwAAMxEzEQEzAwEjAQcVYjgBU0b9ASJE/v54Apz+hAF8/uv+eQFeg9v//wBi/xgCWAKcAiYAMQAAAAYBWOoAAAEAkwAAAkYCnAAFAAAzETMRIRWTOAF7Apz9lDAA//8AkwAAAkYDRwImADMAAAAGAUg4AP//AJMAAAJGAqkCJgAzAAAABgFLGdv//wCT/xgCRgKcAiYAMwAAAAYBWAwAAAEAHwAAAkYCnAANAAAzEQc1NxEzETcVBxUhFZN0dDjKygF7AQVEN0QBYP7Adjd29TAAAQAzAAACRQKcAA8AADMRMxMzEzMRIxEjAyMDIxEzYagEpGE4BK0+rwQCnP2vAlH9ZAJg/aACYP2gAAEAVQAAAiMCnAALAAAzETMBMxEzESMBIxFVWQE5BDhZ/scEApz9ogJe/WQCXv2i//8AVQAAAiMDRwImADkAAAAGAUg0AP//AFUAAAIjA0UCJgA5AAAABwFOAAAApP//AFX/GAIjApwCJgA5AAAABgFY5AD//wBVAAACIwM9AiYAOQAAAAcBVAAAAKkAAgAp//QCTwKoAA8AHwAABSImJjU0NjYzMhYWFRQGBicyNjY1NCYmIyIGBhUUFhYBPFd7QUF7V1d7QUF7Vz5jOTljPj5jOTljDFqcZGScWlqcZGScWjBFhWBhhEVFhGFghUUA//8AKf/0Ak8DRwImAD4AAAAGAUg1AP//ACn/9AJPA0oCJgA+AAAABwFMAAEAo///ACn/9AJPAzkCJgA+AAAABwFAAAEAiv//ACn/9AJPA0cCJgA+AAAABgFGzQD//wAp//QCTwNjAiYAPgAAAAcBSQAtAKwAAwAp//QCTwKoABgAIgAsAAAFIiYnByc3JjU0NjYzMhYXNxcHFhYVFAYGJzI2NjU0JicBFicBJiMiBgYVFBYBPDpcIzYeO0FBe1c6XiI2HzwgIEF7Vz5jORYU/rg8UwFIO14+YzkXDCglRBlLXZBknFopJUQYTC14R2ScWjBFhWA7XyX+X0hqAaFJRYRhO2H//wAp//QCTwM9AiYAPgAAAAcBVAABAKkAAgAI//QCaQKoABkAKQAAFyImJjU0NjYzMhc1IRUjFTMVIxEzFSE1BgYnMjY2NTQmJiMiBgYVFBYWyz9XLS1YP2IzAQXNoKDQ/vgZSy0rQSUlQSspQSUlQQxWm2lpm1ZnWzD8MP7wMFoxNTBBhWRmhEBAhGZmhEAAAgB3AAACNQKcAAwAFQAAMxEzMhYWFRQGBiMjEREzMjY1NCYjI3fVR2k5OWlHnZdTY2NTlwKcKlRBQFYq/uMBTT9RUD8AAAIAdwAAAjUCnAAOABcAADMRMxUzMhYWFRQGBiMjFTUzMjY1NCYjI3c4nUdpOTlpR52XU2NjU5cCnIkqVEFAViqUxD9RUD8AAAIAKf/0AngCqAAUACgAAAUiJiY1NDY2MzIWFhUUBgcXBycGBicyNyc3FzY2NTQmJiMiBgYVFBYWATxXe0FBe1dXe0EbGl4kWSJhPFw8rSSmERQ5Yz4+Yzk5YwxanGRknFpanGRAcCxZJVQoLDBIoyacI1s3YYRFRYRhYIVFAAIAdgAAAjECnAAOABkAADMRMzIWFhUUBgcTIwMjEREzMjY2NTQmJiMjdsxFZjluWdJHynKRMk4uLk4ykQKcKVNBWFoH/toBI/7dAVMcPjIzPhwA//8AdgAAAjEDRwImAEoAAAAGAUgcAP//AHYAAAIxA0UCJgBKAAAABwFO/+gApAABAEn/9AIwAqgAMQAABSImJic3HgIzMjY2NTQmJycuAjU0NjYzMhYWFwcuAiMiBgYVFBYXFx4CFRQGBgE6L2ROEDYNOk0oNVUyP1lfK00vO2ZBLVRFFDcPNUAeLU4vSTFgO1gxRHAMH0M2FSs4GiJCMC48FhgLJkI0M1AuGDMnFR0mFBw6LC81DRkPKkY4O1cv//8ASf/0AjADRwImAE0AAAAGAUg0AP//AEn/9AIwA0UCJgBNAAAABwFOAAAApAABAEn/TwIwAqgAUQAABSImJzcWFjMyNjU0JiMiBiMiJjc3LgInNx4CMzI2NjU0JicnLgI1NDY2MzIWFhcHLgIjIgYGFRQWFxceAhUUBgYHBzY2MzIWFhUUBgYBMxMtEQ4VHQ8cFBIXCxcICwUHGyxYRQ42DTpNKDVVMj9ZXytNLztmQS1URRQ3DzVAHi1OL0kxYDtYMUBrQBcKEQQVGw4SJ7EOChkKBxwKDhAGEgosBCJAMRUrOBoiQjAuPBYYCyZCNDNQLhgzJxUdJhQcOiwvNQ0ZDypGODpVMAIkAgIQGQ4PJRoA//8ASf8YAjACqAImAE0AAAAGAVj0AAABAFH/9AJTAqgALgAABSImJic3FhYzMjY1NCYmIyIGBycTJiYjIgYGFREjETQ2NjMyFhcVBzYWFhUUBgYBpDJCJwkyCTU0MkYiNx4PGQketjNfKz5ZLzhDc0g4ej6qM1c0MlAMHzQdEyUsREAtOhsFBDIBCwsNIFBJ/kEBv1llKxQPMvkIKVU7PFApAAEAPwAAAjkCnAAHAAAhESM1IRUjEQEg4QH64QJsMDD9lP//AD8AAAI5A0UCJgBTAAAABwFOAAAApAABAD//TwI5ApwAKAAAIREjNSEVIxEjBzY2MzIWFhUUBgYjIiYnNxYWMzI2NTQmIyIGIyImNzcBIOEB+uEJHwoRBBUbDhInHxMtEQ4VHQ8cFBIXCxcICwUHIgJsMDD9lDACAhAZDg8lGg4KGQoHHAoOEAYSCjf//wA//xgCOQKcAiYAUwAAAAYBWPQAAAEAVf/0AiMCnAASAAAFIiY1ETMRFBYzMjY2NREzERQGATt5bThTWz1OJThuDIF/Aaj+VGVnLltDAaz+WH+BAP//AFX/9AIjA0cCJgBXAAAABgFINAD//wBV//QCIwNKAiYAVwAAAAcBTAAAAKP//wBV//QCIwM5AiYAVwAAAAcBQAAAAIr//wBV//QCIwNHAiYAVwAAAAYBRswA//8AVf/0AiMDYwImAFcAAAAHAUkALACs//8AVf/0AiMDHgImAFcAAAAHAVYAAACTAAEAVf9YAiMCnAApAAAFIiYmNTQ2NwYGLgI1ETMRFBYzMjY2NREzERQGBwYGFRQWMzI2NxUGBgGpFyYVFREjUU5AJjhTWz1OJTgxNR0cIhQIEAcPFagSIRcYLRMFARU2Y08Bq/5UZWcuW0MBrP5YVXIfETEaGRgDBCcGAwADAFX/9AIjA2cADwAbAC4AAAEiJiY1NDY2MzIWFhUUBgYnMjY1NCYjIgYVFBYTIiY1ETMRFBYzMjY2NREzERQGAT8cLRsbLRwcKRgYKR8XICAXFiEhFXltOFNbPU4lOG4CphosGhosGxssGhosGisfFhYfHxYWH/0jgX8BqP5UZWcuW0MBrP5Yf4EAAAEAMQAAAkcCnAAHAAAhAzMTMxMzAwEe7TrPBNA57gKc/agCWP1kAAABAA0AAAJsApwADwAAMwMzEzMTMxMzEzMDIwMjA4p9OGcEbz5tBGc3fUdpBGoCnP2vAlH9rwJR/WQCNv3K//8ADQAAAmwDRwImAGEAAAAGAUg2AP//AA0AAAJsA0oCJgBhAAAABwFMAAIAo///AA0AAAJsAzkCJgBhAAAABwFAAAIAiv//AA0AAAJsA0cCJgBhAAAABgFGzgAAAQAjAAACVQKcAA0AADMTAzMTMxMzAxMjAyMDI/XnQsYExkTn9UPUBNQBWQFD/ugBGP69/qcBLf7TAAEAJAAAAlQCnAAJAAAhEQMzEzMTMwMRASH9QtUE00L7AQwBkP6rAVX+cP70AP//ACQAAAJUA0cCJgBnAAAABgFINQD//wAkAAACVANKAiYAZwAAAAcBTAABAKP//wAkAAACVAM5AiYAZwAAAAcBQAABAIr//wAkAAACVANHAiYAZwAAAAYBRs0AAAEANgAAAkECnAAJAAAzNQEhNSEVASEVNgG//lAB7f5BAc48Ai4yPP3SMgD//wA2AAACQQNHAiYAbAAAAAYBSDgA//8ANgAAAkEDRQImAGwAAAAHAU4ABACk//8ANgAAAkEDOwImAGwAAAAGAUMEAAACAFj/9AIDAfoAHAAnAAAFIiY1NDY3NTQmIyIGByc2NjMyFhUVFBYXIycGBicyNjY1NQYGFRQWAQpPY7GzPkg6RAk4FGZEYloLBywUEFdPMU8vmJVCDEVBWlMNCkFNMiYNPD1iXnhGWyFRIjsvH0c6RAo/QSgyAP//AFj/9AIDArgCJgBwAAAABgFHLAD//wBY//QCAwKfAiYAcAAAAAYBUPoA//8AWP/0AgMCpwImAHAAAAAGAUz6AP//AFj/9AIDAq8CJgBwAAAABgFA+gD//wBY//QCAwK4AiYAcAAAAAYBRccA//8AWP/0AgMCiwImAHAAAAAGAVb6AAACAFj/WAIDAfoALwA6AAAFIiYmNTQ2NycGBiMiJjU0Njc1NCYjIgYHJzY2MzIWFRUUFhcGBhUUFjMyNjcVBgYnMjY2NTUGBhUUFgHTFyYVKikREFdST2Oxsz5IOkQJOBRmRGJaCwcoLR4VChEHDxXSMU8vmJVCqBIhFyMyFEYiO0VBWlMNCkFNMiYNPD1iXnhGWyENLRoWFQQDJwYDyx9HOkQKP0EoMv//AFj/9AIDAvsCJgBwAAAABgFS+gD//wBY//QCAwKUAiYAcAAAAAYBVPoAAAMACv/0AmwB+gAvADsAQwAAFyImNTQ2Njc1NCYjIgYHJzY2MzIWFzY2MzIeAgchFB4CMzI2NxcGBiMiJicGBicyNjY1NQ4CFRQWEzM0JiYjIgaTPUwtcmUrMictCTQUTTEzQQ0XSS4vQioQA/7VGyswFSEsEy0UQzs5TRUUSDchNiFUWiMu1fQWMywsSQxEPTBGLAcgQlA1Jgw8PTQxMDUtS14wPlIwFCEiECU6PTM3OSofST0xBSI0IyUzAQAoUTdOAAACAGT/9AI9AsQAFAAhAAAFIiYmJwcjETMRPgIzMhYWFRQGBicyNjU0JiMiBhUUFhYBUy1INBENKDYSNUcrRWo7O2pKU2VlU1JiLVEMGywaVQLE/tEeLRo9dFJSdD0wbmVlbnFiQF80AAEAZf/0AiMB+gAeAAAFIiYmNTQ2NjMyFhcHJiYjIgYGFRQWFjMyNjcXDgIBV0duPTpvT0RhHDEUTTIxVzY1VTE0UhMzDjdRDENzSUh4RzwwEyYpLl5JR10tKiMTGTEg//8AZf/0AiMCuAImAHwAAAAGAUdQAP//AGX/9AIjAqECJgB8AAAABgFOHgAAAQBl/08CIwH6AD4AAAUiJic3FhYzMjY1NCYjIgYjIiY3Ny4CNTQ2NjMyFhcHJiYjIgYGFRQWFjMyNjcXDgIHBzY2MzIWFhUUBgYBTxMtEQ4VHQ8cFBIXCxcICwUHG0BhNjpvT0RhHDEUTTIxVzY1VTE0UhMzDjVMMxcKEQQVGw4SJ7EOChkKBxwKDhAGEgosB0RvREh4RzwwEyYpLl5JR10tKiMTGC8hAiQCAhAZDg8lGgD//wBl//QCIwKvAiYAfAAAAAYBQh4AAAIAO//0AiUCxAAVACIAAAUiJiY1NDY2MzIWFxEzERQWFyMnBgYnMjY2NTQmIyIGFRQWASVFaTw8aUVDXRk2BA0yFxlbPjdQLWJSU2VlDD10UlJ0PTkrAS798xJeR1QoODA0X0Bhcm5lZW4AAAMALP/0AlcC0AAUACAAKwAABSImJjU0NjMyFhcRMxEUFhcjJwYGJzI2NTQmIyIGFRQWATc2NjMyFhUUBwcBAT9gNnZfOVAZNgQNMhcZTjRIVVVISllZAU8SBAwPCRIKJAw9dFJ7iDkrAS798xJeR1QoODByYWFybmVlbgH6gRkYCw8IH3EAAgA7//QCVQLEAB0AKgAABSImJjU0NjYzMhYXNSM1MzUzFTMVIxEUFhcjJwYGJzI2NjU0JiMiBhUUFgElRWk8PGlFQ10Zqak2QUEEDTIXGVs+N1AtYlJTZWUMPXRSUnQ9OSubKGtrKP6GEl5HVCg4MDRfQGFybmVlbgACAD3/9AIpAtEAJQA1AAAFIi4CNTQ+AjMyFhYXJiYnByc3JiYHNTYWFzcXBxYWFRQOAicyNjY1NCYmIyIGBhUUFhYBLyxWRiopRVUrLlU/DAUqIT0cORplR0p1KikcJTUoGThhSC9VNjdVLy5WNjdWDB48WTs7WTweHjkpbHQiOxs5EyYGLQYmHCgbJjeXZEJ5XzYwKlM/QFUrKlRAP1UqAAACAFT/9AImAfoAGQAiAAAFIiYmNTQ2NjMyHgIHIRQeAjMyNjcXBgYBITQmJiMiBgYBQ01rNz1rRTpYORoD/mgkOUIeNUETNhlb/voBXSVKOipNNQxFdUdQdUAtS14wPVAvFCUbEyU4AS0mTjUgSgD//wBU//QCJgK4AiYAhQAAAAYBRzwA//8AVP/0AiYCoQImAIUAAAAGAU4KAP//AFT/9AImAqcCJgCFAAAABgFMCgD//wBU//QCJgKvAiYAhQAAAAYBQAoA//8AVP/0AiYCrwImAIUAAAAGAUIKAP//AFT/9AImArgCJgCFAAAABgFF1wD//wBU//QCJgKLAiYAhQAAAAYBVgoAAAIAVP9YAiYB+gAsADUAAAUiJiY1NDcGJiY1NDY2MzIeAgchFB4CMzI2NxcGBgcGBhUUFjMyNjcVBgYBITQmJiMiBgYBqhcmFSVVhk49a0U6WDkaA/5oJDlCHjVBEzYNJxgcGiIUCBAHDxX+1wFdJUo6Kk01qBIhFzAnDzZ8WVB1QC1LXjA9UC8UJRsTEyQPES8ZGRgDBCcGAwHJJk41IEoAAAEAbQAAAiQCzAAUAAAhESM1MzU0NjYzMxUjIgYVFTMVIxEBDaCgEi8sqq4fFJGRAcQsZjAzEzAaLGYs/jwAAgBC/0YCEwH6ACEALgAABSImJic3FhYzMjY2NTUGBiMiJiY1NDY2MzIWFzczERQGBicyNjY1NCYjIgYVFBYBNy1WPgo3ClA8L0orGlpBQ2g7O2hDQVsZDig3Y0w2TytgUFFjY7oYMigPJiodPS9hKDU7cE9PcD05Kln+DDhSLO4yWj1ccWxhYGn//wBC/0YCEwKfAiYAjwAAAAYBUPcAAAMAQv9GAhMC8wAPADEAPgAAASYmNTQ2NjcXDgIVFBYXAyImJic3FhYzMjY2NTUGBiMiJiY1NDY2MzIWFzczERQGBicyNjY1NCYjIgYVFBYBORQTHS0WHhAmGxITLS1WPgo3ClA8L0orGlpBQ2g7O2hDQVsZDig3Y0w2TytgUFFjYwI/ESQWIC4aARkBDx4aEyIM/PUYMigPJiodPS9hKDU7cE9PcD05Kln+DDhSLO4yWj1ccWxhYGkA//8AQv9GAhMCrwImAI8AAAAGAUL3AAABAHIAAAIIAsQAFQAAMxEzET4CMzIWFhURIxE0JiMiBhURcjYMLkQtP1AmNjxJSF0CxP7fEygcL1Ey/rgBNz9UYFn+7wAAAQAxAAACCALEAB0AADMRIzUzNTMVMxUjFT4CMzIWFhURIxE0JiMiBhURckFBNqmpDC5ELT9QJjY8SUhdAjEoa2sojhMoHC9RMv64ATc/VGBZ/u///wBzAAACEQK3AiYAlgAAAAYBRPsAAAEAcwAAAhEB8AAJAAAzNTMRIzUzETMVc7Sb0bQsAZgs/jws//8AcwAAAhECuAImAJYAAAAGAUctAP//AHMAAAIRAqcCJgCWAAAABgFM+wD//wBzAAACEQKvAiYAlgAAAAYBQPsA//8AcwAAAhECrwImAJYAAAAGAUL7AP//AHMAAAIRArgCJgCWAAAABgFFyAD//wBzAAACEQKLAiYAlgAAAAYBVvsAAAIAc/9YAhECtwAdACkAADM1MxEjNTMRMxUGBhUUFjMyNjcVBgYjIiYmNTQ2NwMiJjU0NjMyFhUUBnO0m9G0KC0eFQoRBw8VDBcmFSAflxMdHRMUHBwsAZgs/jwsDS0aFhUEAycGAxIhFx4tEwJXHRMUHBwUEx0AAAQAbv9fAgoCtwAPABUAIQAtAAAFNTMyNjY1ESM1MxEUBgYjJxEjNTMRAyImNTQ2MzIWFRQGISImNTQ2MzIWFRQGASVJKicLPXMSPkLDPXMmEx0dExQcHAELEx0dExQcHKEwEC0qAc4s/gc5Qh2hAcQs/hACVx0TFBwcFBMdHRMUHBwUEx3//wBk/14BhQK3AiYAoAAAAAYBRBkAAAEAZP9eAXsB8AAPAAAXNTMyNjY1ESM1MxEUBgYjZIUqJwub0RI+QqIwEC0qAc8s/gY5Qh0A//8AZP9eAc0CuAImAKAAAAAGAUdLAAABAJgAAAI2AsQACwAAMxEzESUzBxMjAwcVmDYBBEXU80XTUALE/jH7yv7aAQJMtv//AJj/GAI2AsQCJgCiAAAABgFYAQAAAQB8AAACEALEAA4AACEiJiY1ESM1MxEUFjMzFQFSJysRc6kRIbkSKSQCOSz9nhwWMP//AHwAAAIQA28CJgCkAAAABgFIASj//wB8AAACEALRAiYApAAAAAYBS20D//8AfP8YAhACxAImAKQAAAAGAVgqAAABAHwAAAIQAsQAFgAAISImJjU1BzU3ESM1MxE3FQcVFBYzMxUBUicrEUBAc6leXhEhuRIpJLQqMCoBVSz+oj4wPtQcFjAAAQBHAAACMwH6ACIAADMRMxc2NjMyFhc2NjMyFhURIxE0JiMiBhURIxE0JiMiBhURRyoJDDUrKjULCzksPjU2ISknNDYgKCc2AfA3GCkkIBkrSjn+iQFmLTc9Pf6wAWctNj49/rEAAQByAAACCAH6ABUAADMRMxc+AjMyFhYVESMRNCYjIgYVEXIoDAwtRi4/UCY2PElIXQHwUBMqHS9RMv64ATc/VGBZ/u8A//8AcgAAAggCuAImAKoAAAAGAUcyAP//AHIAAAIIAqECJgCqAAAABgFOAAD//wBy/xgCCAH6AiYAqgAAAAYBWO4A//8AcgAAAggClAImAKoAAAAGAVQAAAACAEb/9AIyAfoAEwAjAAAFIi4CNTQ+AjMyHgIVFA4CJzI2NjU0JiYjIgYGFRQWFgE8K1dILCxIVyssV0grK0hXLDFXNzdXMTFXNzdXDB4/YkREYj8eHj9iRERiPx4wLV5ISV0tLV1JSF4tAP//AEb/9AIyArgCJgCvAAAABgFHMgD//wBG//QCMgKnAiYArwAAAAYBTAAA//8ARv/0AjICrwImAK8AAAAGAUAAAP//AEb/9AIyArgCJgCvAAAABgFFzQD//wBG//QCMgK3AiYArwAAAAYBSSwAAAMARv/0AjIB+gAbACYAMQAABSImJwcnNyYmNTQ+AjMyFhc3FwcWFhUUDgInMjY2NTQmJwEWFicBJiYjIgYGFRQWATwrViMuGy4ZHixIVysrVSMvGy8aHitIVywxVzcUEv7mGkN2ARoaQyMxVzcUDB0eMBsxH1Y3RGI/Hh0eMhsyH1Y4RGI/HjAtXkgrQxn+1hgYTQEqGBctXUkqQwD//wBG//QCMgKUAiYArwAAAAYBVAAAAAMABP/0AnYB+gAkADQAPAAABSImJwYGIyImJjU0NjYzMhYXNjYzMh4CByEGFhYzMjY3FwYGJTI2NjU0JiYjIgYGFRQWFjczNCYmIyIGAdc5SxQWUSkqTzIyUCwuTxYUSTEtPygPA/7nASo7GiAnEy0TP/6jIDgjIzggHzcjIzfL4hUuKChFDEQ3QDs2c1pbcjY8QTtCLUteMFJdJSEhECU5MC1eSEldLS1dSUheLfooUTdOAAIAZP9eAj0B+gAUACAAABcRMxc+AjMyFhYVFAYGIyImJicVNzI2NTQmIyIGFRQWZCgQETVGK0VqOztqRS1INBC0U2VlU1JiYqICklgcLBo9dFJSdD0bLBn2xm5lZW50X2FyAAACAGT/XgI9AsQAFAAgAAAXETMRPgIzMhYWFRQGBiMiJiYnFTcyNjU0JiMiBhUUFmQ2EjVHK0VqOztqRS1INBC0U2VlU1JiYqIDZv7RHi0aPXRSUnQ9Gywa98ZuZWVucWJhcgAAAgA7/1ICeAH6AB8AKwAABSImJjU1BgYjIiYmNTQ2NjMyFhc3MxEUFjMyNjcVBgYlMjY1NCYjIgYVFBYCQB0sGRtbQ0VpPDxpRUNdGQ4oGRsNGAsNG/7aU2FiUlNlZa4VNC2OKTk9dFJSdD05K1r92SUiBwcwBwfScmFhcm5lZW4AAAEAogAAAggB9gASAAAzETMXNjYzMhYXFSYmIyIGBhUVoigQIHtJEisNFi0UPGI7AfB9QUIFAzwGBzJlTtr//wCiAAACCAK4AiYAuwAAAAYBRzEA//8AogAAAggCoQImALsAAAAGAU7/AAABAG7/9AIKAfoAKgAABSImJic3FhYzMjY1NCYnJyYmNTQ2NjMyFhcHJiYjIgYVFBYXFxYWFRQGBgFELFdDEDUOVEBGS0c3T0BMMVY2RmQUMw1OMjhPOS1PTlY0WQwaNysULjI1KS8nDBEONzkpPSE0LxQjJCspIiUKERA9QS1CIwD//wBu//QCCgK4AiYAvgAAAAYBRzAA//8Abv/0AgoCoQImAL4AAAAGAU7+AAABAG7/TwIKAfoASQAABSImJzcWFjMyNjU0JiMiBiMiJjc3JiYnNxYWMzI2NTQmJycmJjU0NjYzMhYXByYmIyIGFRQWFxcWFhUUBgYHBzY2MzIWFhUUBgYBPBMtEQ4VHQ8cFBIXCxcICwUHGzxqFTUOVEBGS0c3T0BMMVY2RmQUMw1OMjhPOS1PTlYxVTYXChEEFRsOEiexDgoZCgccCg4QBhIKLAU8OhQuMjUpLycMEQ43OSk9ITQvFCMkKykiJQoRED1BLEAkAiQCAhAZDg8lGv//AG7/GAIKAfoCJgC+AAAABgFY/QAAAQBi//ICQgKoAC8AAAUiIicnFhYzMjY2NTQmJiMjNTI2NTQmIyIGBhURIxE0NjYzMhYWFRQGBxYWFRQGBgFGBw4IDwwWCz5YLzxlPgxNS008KEYqNi1cRzpULiw3UGdDcg4BMAEBJ0w5N1EuMEEoJzUfSD7+LQHVPF84J0AkKUAUEG9US2EvAAEAggAAAf0CbAAUAAAhIiYmNREjNTM1MxUzFSMRFBYzMxUBWSguE25uNtfXFSJuEjIwAVAsfHws/rErGjD//wCCAAAB/QL8AiYAxAAAAAYBS0UuAAEAgv9PAf0CbAAzAAAFIiYnNxYWMzI2NTQmIyIGIyImNzcmJjURIzUzNTMVMxUjERQWMzMVIwc2NjMyFhYVFAYGAUYTLREOFR0PHBQSFwsXCAsFByMrIG5uNtfXFSJuax8KEQQVGw4SJ7EOChkKBxwKDhAGEgo4BTA+AVAsfHws/rErGjAwAgIQGQ4PJRoA//8Agv8YAf0CbAImAMQAAAAGAVgHAAABAHb/9AH/AfAAFAAABSImNREzERQWFjMyNjURMxEjJwYGASVeUTYYNzBFWTYoDBFRDGZPAUf+yixEJmBYART+EFAhOwD//wB2//QB/wK4AiYAyAAAAAYBRzAA//8Adv/0Af8CpwImAMgAAAAGAUz+AP//AHb/9AH/Aq8CJgDIAAAABgFA/gD//wB2//QB/wK4AiYAyAAAAAYBRcsA//8Adv/0Af8CtwImAMgAAAAGAUkqAP//AHb/9AH/AosCJgDIAAAABgFW/gAAAQB2/1gB/wHwACcAAAUiJjURMxEUFhYzMjY1ETMRBgYVFBYzMjY3FQYGIyImJjU0NjcnBgYBJV5RNhg3MEVZNigtHhUKEQcPFQwXJhUtKwoRUQxmTwFH/sosRCZgWAEU/hANLRoWFQQDJwYDEiEXIzMWQiE7//8Adv/0Af8C+wImAMgAAAAGAVL+AAABAEIAAAI2AfAABwAAIQMzEzMTMwMBJ+U7vwS8OuUB8P5YAaj+EAAAAQAjAAACVQHwAA8AADMDMxMzEzMTMxMzAyMDIwOkgTZhBGUyaQReNYAtagRpAfD+bQGT/m8Bkf4QAaL+Xv//ACMAAAJVArgCJgDSAAAABgFHMgD//wAjAAACVQKnAiYA0gAAAAYBTAAA//8AIwAAAlUCrwImANIAAAAGAUAAAP//ACMAAAJVArgCJgDSAAAABgFFzQAAAQBOAAACKwHwAA0AADM3JzMXMzczBxcjJyMHTsnCQKQEqD/CyUGtBKz989HR9PzZ2QABAEL/XgI2AfAAEgAAFzUzMjY3NwMzEzMTMwEOAyNiTSIfECHfO78EvDr++AoUGygfojAVIkYB5f5ZAaf9xRYhFgoA//8AQv9eAjYCuAImANgAAAAGAUcyAP//AEL/XgI2AqcCJgDYAAAABgFMAAD//wBC/14CNgKvAiYA2AAAAAYBQAAA//8AQv9eAjYCuAImANgAAAAGAUXNAAABAGkAAAIYAfAACQAAMzUBITUhFQEhFWkBY/6lAZ7+nQFsMAGQMDD+cDAA//8AaQAAAhgCuAImAN0AAAAGAUc9AP//AGkAAAIYAqECJgDdAAAABgFOCwD//wBpAAACGAKvAiYA3QAAAAYBQgsAAAIAqAFPAc0CtQAcACYAAAEiJjU0Njc0JiYjIgYHJzY2MzIWFRUUFhcjJwYGJzI2NTUGBhUUFgEWMzt0dAckKiIyCCoLRjQ4SwcJLw4VPyA6OWZPIAFPLy46NwcWMCIeGwklNEJQZisqETMgGyZHLhQHJiccGQACAJ4BTwHaArUADwAbAAABIiYmNTQ2NjMyFhYVFAYGJzI2NTQmIyIGFRQWATsoSC0tSCgoSS4uSSgxPDwxMDs7AU8nTz08UCcnUDw9TycpSz8/S0s/P0sAAgAjAAACVQKcAAMABwAAMxMzEyUhAyMj/Dv7/hgBns0EApz9ZDACMQAAAQAdAAACWwKoACsAADM1MzUuAjU0PgIzMh4CFRQGBgcVMxUjNT4CNTQmJiMiBgYVFBYWFxUdqzFKKCtNZTo6ZU0rKEkyq9sxSSk/aT8/aT8pSjAwQxlTaTg+bFEtLVFsPjhpUxlDMJMQRmA2R3BCQnBHNmBGEJMAAQCB/14CLQHwAB4AABcRMxEUFhYzMjY1ETMRFBYzMxUjIiYnDgIjIiYnFYE2FjQtQVM2DxwKDDclAwsoPCooQAqiApL+yixEJmBYART+cBwWLigmFSkcGhbGAAABAEkAAAIgAfAAFAAAMxEjNSEVIxEUFjMzFSMiJiY1ESMRkEcB11ISHyElLSsM0QHELCz+oyUSMBQ3NAFF/jwAAQEI//QBcABcAA8AAAUiJiY1NDY2MzIWFhUUBgYBPA4YDg4YDg8XDg4XDA4YDg8XDg4XDw4YDgABAQj/dgFxAFwADwAABTcmJjU0NjYzMhYVFAYHBwEIMRQcDhgOFh4CBTmKfgIdFQ8XDh4WBA4LlQD//wEI//QBcAH9AiYA6gAAAAcA6gAAAaH//wEI/3YBcQH9AiYA6wAAAAcA6gABAaEAAwA2//QCQgBcAAsAFwAjAAAXIiY1NDYzMhYVFAYzIiY1NDYzMhYVFAYzIiY1NDYzMhYVFAZqFh4eFhYeHrwWHh4WFh4evBYeHhYWHh4MHhYWHh4WFh4eFhYeHhYWHh4WFh4eFhYeAAACAQ3/9AFrApwABQARAAAlAzUzFQMHIiY1NDYzMhYVFAYBLQw2DA8THBwTFBsbqgFHq6v+ubYcExQbGxQTHAAAAgEN/1YBawH+AAUAEQAABTUTMxMVAyImNTQ2MzIWFRQGASEMHgwbExwcExQbG6qrAUf+uasCShwTFBsbFBMcAAIAhv/0AgUCqAAhAC0AACU1NDY2NzY2NTQmIyIGBgcnPgIzMhYWFRQGBgcOAhUVByImNTQ2MzIWFRQGARAQKSctMk06MTwdAzUGNVU0LlU4HDkqHRwHGxMcHBMUGxuiKCY0Lh0iSCw2PSg5GgkxSCkkSDcoPzsjGB8gGyyuHBMUGxsUExwAAgBz/0oB8gH+ACEALQAABSImJjU0NjY3PgI1NTMVFAYGBwYGFRQWMzI2NjcXDgIDIiY1NDYzMhYVFAYBLi1WOB04Kh4bBzYPKictMk06MTweAjUFNlUVExwcExQbG7YkSTYpPjsjGR4hGiwoJTUuHSJILDY9KDoZCTBJKQJWHBMUGxsUExwA//8BCADJAXABMQIHAOoAAADV//8BCADJAXABMQIHAOoAAADV//8BCADJAXABMQIHAOoAAADVAAEAlgBYAeIBpAAPAAAlIiYmNTQ2NjMyFhYVFAYGATwuSy0tSy4uSy0tS1gtTC0uSy0tSy4tTC0AAQCNAU4B7AKaAA4AABMnNyc3FzUzFTcXBxcHJ+gkVo0NjSuNDY1YI1gBThp3Kistk5MtKyp3GnUAAAIAJgAAAlICnAAbAB8AADM3IzczNyM3MzczBzM3MwczByMHMwcjByM3Iwc3MzcjhRt6B3gZeQd4Gy8btxsvG3kHdxl4B3cbLxu3GyC4GbjGK7orxsbGxiu6K8bGxvG6AAEAPP+mAjwCxAADAAAXATMBPAHLNf41WgMe/OIAAAEAPP+mAjwCxAADAAAFIwEzAjw1/jU1WgMeAAABALMA3QHFARQAAwAANzUhFbMBEt03NwABAGkA3AIPAREAAwAANzUhFWkBptw1NQABACMA3AJVAREAAwAANzUhFSMCMtw1NQAB//7/ywJ6AAAAAwAABzUhFQICfDU1NQABAMP/XgHiAsQADQAABSYmNTQ2NzMGBhUUFhcBmmdwcGdIb3l5b6Jb43V141tf4XNz4V8AAQCW/14BtQLEAA0AABc2NjU0JiczFhYVFAYHlm95eW9IaG9vaKJf4XNz4V9b43V141sAAAEAl/9eAdUCxAAlAAAFIiYmNTU0Jic1NjY1NTQ2NjMzFSMiBhUVFAYHFRYWFRUUFjMzFQFaLCsNJTo6JQ0rLHuAGBIhIiIhEhiAohMpIeM0KAIqAig04yEpEyoSFuA9NwoGCjY+4BYSKgABAKP/XgHhAsQAJQAAFzUzMjY1NTQ2NzUmJjU1NCYjIzUzMhYWFRUUFhcVBgYVFRQGBiOjgBgSISIiIRIYgHstKg0lOjolDSotoioSFuA+NgoGCjc94BYSKhMpIeM0KAIqAig04yEpEwAAAQDU/14B1QLEAAcAABcRIRUjETMV1AEBy8uiA2Yo/OooAAEAo/9eAaQCxAAHAAAXNTMRIzUhEaPLywEBoigDFij8mv//AQj/dgFxAFwCBgDrAAAAAgCw/3YByABcAA8AHwAAFzcmJjU0NjYzMhYVFAYHBzM3JiY1NDY2MzIWFRQGBwewMRQcDhgOFh4CBTmGMRQcDhgOFh4CBTmKfgIdFQ8XDh4WBA4LlX4CHRUPFw4eFgQOC5UAAAIAsAG2AcgCnAAPAB8AAAEiJjU0Njc3MwcWFhUUBgYjIiY1NDY3NzMHFhYVFAYGAZMWHgMEOSkxFBwOF74WHgMEOSkxFBwOFwG2HhYFDQuVfgEeFQ4YDh4WBQ0LlX4BHhUOGA4AAAIAsAHCAcgCqAAPAB8AAAE3JiY1NDY2MzIWFRQGBwcjNyYmNTQ2NjMyFhUUBgcHAV8xFBwOGA4WHgIFOdgxFBwOGA4WHgIFOQHCfgIdFQ8XDh4WBA4LlX4CHRUPFw4eFgQOC5UAAAEBBwHCAXACqAAPAAABIiY1NDY3NzMHFhYVFAYGATsWHgMEOSkxFBwOFwHCHhYFDQuVfgEeFQ4YDgABAQgBwgFxAqgADwAAATcmJjU0NjYzMhYVFAYHBwEIMRQcDhgOFh4CBTkBwn4CHRUPFw4eFgQOC5UAAgB0ACgCBAHMAAUACwAAJSc3MwcXIyc3MwcXAciUlDyUlPyUlDyUlCjS0tLS0tLS0gACAHQAKAIEAcwABQALAAAlNyczFwcjNyczFwcBNJSUPJSU/JSUPJSUKNLS0tLS0tLSAAEA1AAoAaQBzAAFAAAlJzczBxcBaJSUPJSUKNLS0tIAAQDUACgBpAHMAAUAADc3JzMXB9SUlDyUlCjS0tLSAAACAM0BsAGrApwAAwAHAAATJzMHMyczB94RORGOETkRAbDs7OzsAAEBIAGwAVkCnAADAAABJzMHATEROREBsOzsAAABAGH/VAIXArYAIwAAFyImJzUWFjMyNjY1ESM1MzU0NjYzMxUjIgYVFTMVIxEUDgKcESEJCCYWKDYcubkKKzGSnRwPyMgfNT+sBgQwBQokRDABsClGLzgZLR4sTyn+akhYLRAAAQBh//YCFgKcACIAABciJiY1NDY2MzIXBxEzHgIXHgIVFAYHJy4CJzcRFAYGwxstGhsuHC4bByMDHjMjHjUhFRcDCDlVMw0cLwobLhwcLhsgBQIBIjQwHBg0PigTOB4BO1c8DxP+nyg2GwAAAgAP/6MCaQJZADwASgAABSIuAjU0NjYzMh4CFRQGBiMiJicGBiMiJiY1NDY2MzIWFxUUFjMyNjY1NCYmIyIGBhUUFhYzMjY3FQYnMjY1NSYmIyIGBhUUFgFGO29ZNE+LWTBoWDcmOyEfLgkVOx8fOiUyUCsaNRkhFBwiED5wTEt1Q0d2Rh5AHDlaJTgNGBAaOSg0XSxXg1homlYhSXlXS1wqJBgkGyRJNUtZJwsJ6CUcNEwkUXlCR4ZeZodCERI1HNNBL50FAhpEQDs7AAADABb/9QJpAqkAJAAvADsAABciJiY1NDY2NyYmNTQ2NjMyFhYVFAYGBxc2NjczFAYHFyMnBgYnMjY3JwYGFRQWFhM2NjU0JiMiBhUUFuU+XjMwUzIfLydFLSRFLStCIqALDAE2FBSGRlwnc0hBYB66RFosRkIzRT0lLDgsCy1UOjVPOhUiTCkoQSYcPjMrQS0Psx9HLTdfKJZoNj0wODHSHlA/MD8fAXATQjExLzUpJT0AAQBl/14CHQKcABAAAAURJiY1NDY2MzMVIxEjESMRARxYXzZiQ90kNXOiAcYNaEY0VjMv/PEDD/zxAAIAYf9TAhcCqAA3AEUAAAUiJic3FhYzMjY1NCYmJycmJjU0NjcmJjU0NjYzMhYXByYmIyIGFRQWFhcXFhYVFAYHFhYVFAYGEzY2NTQmJycGBhUUFhcBOUZiEDoLOTo0RBIwLl9GO0RDMCIwTitHYRA6Cjo6NEQSMC5fRzpEQzAiME0DNUk4OGY0Sjg4rUE7DCkyMyQTICMZNCZEMy5IHCA3Iik8IUE7DCkyMyQTHyQZNCZEMy5IHB84Iik8IQEXFTsmJjQfOBU7JiY0HwAAAwAU//MCZAKpABMALgA+AAAFIi4CNTQ+AjMyHgIVFA4CJyImNTQ2MzIWFwcmJiMiBhUUFjMyNjcXDgIHMjY2NTQmJiMiBgYVFBYWATxDbU4qKk5tQ0NtTioqTm0+RFVXQzI4DTINJBcmODglHSINMgkeLypLbz09b0tLbz09bw02X35ISH5fNjZffkhIfl82nGVbXGM0HBQeGUhKR0wZHhQRJRpsUYhSU4dRUYdTUohRAAQAFP/zAmQCqQATACMAMgA7AAAFIi4CNTQ+AjMyHgIVFA4CJzI2NjU0JiYjIgYGFRQWFicRMzIWFhUUBgcXIycjFTUzMjY1NCYjIwE8Q21OKipObUNDbU4qKk5tQ0tvPT1vS0tvPT1vIW4oNx0jKFw+VTI1JCMpIzANNl9+SEh+XzY2X35ISH5fNjBRiFJTh1FRh1NSiFF/AW8cMiIjOQ6VjIy5JCImHgAAAgAMAVcCawKcAAcAFwAAExEjNTMVIxEzETMTMxMzESMRIwMjAyMRZ1viW49BYgRgQiwEYCliBAFXASAlJf7gAUX+8QEP/rsBD/7xAQ/+8QACAMIBtQG2AqkADwAbAAABIiYmNTQ2NjMyFhYVFAYGJzI2NTQmIyIGFRQWATwhOCEhOCEiNyEhNyIhLi4hIC8vAbUhNyEiOCEhOCIhNyEqLyAiLS0iIC8AAQEh/14BVwNGAAMAAAURMxEBITaiA+j8GAACASH/XgFXA0YAAwAHAAAFETMRAxEzEQEhNjY2ogGL/nUCXQGL/nUAAQBlAAACEwKcAAsAACERIzUzNTMVMxUjEQEivb01vLwBvTOsrDP+QwAAAQBjAAACFQKcABMAACE1IzUzNSM1MzUzFTMVIxUzFSMVASXCwsLCL8HBwcGrPMw9rKw9zDyrAAACADv/9AI8AfUAGQAiAAAFIi4CNTQ+AjMyFhYVIRUWFjMyNjcXBgYDITUmJiMiBgcBOylaTTAjQ148R3RG/lkhVDE4XSEPI2biAU4hVjExVCEMHkFoSSdUSS1Fc0a3HiIsJQIqMQEQox8jIx8AAAIAev+mAg4CSgAdACUAAAU1LgI1NDY2NzUzFR4CFwcmJicRNjY3FwYGBxUnEQ4CFRQWATU6VC0wVDclMUUtDTISPi4uQRE0FVpFJSI7JUdaUAdKbz9BbkgIVlQBIC8WEx0oAv5iAywdEyVCBE6DAZgIM1U6UHMAAAIAUwBZAiUCKQAjADMAADcnNyYmNTQ2Nyc3FzY2MzIWFzcXBxYWFRQGBxcHJwYGIyImJzcyNjY1NCYmIyIGBhUUFhZvHEEXGxsXQRxBHUcoKEgcQRxBFxsbF0EcQRxIKChHHYwuSywsSy4uSywsS1kaQR1HKSlIHEEaQBcaGhdAGkEcSCkpRx1BGkAXGhoXAi1LLi5LLS1LLi5LLQAAAwBJ/6YCMAL2ACkAMQA5AAAFNS4CJzcWFhcRJy4CNTQ2Njc1MxUeAhcHJiYnERceAhUUBgYHFTU2NjU0Ji8CNQYGFRQWFwEhK1lFDzQRXDchMEssM1s6JitQQBM1FVgsJT5XLz9qQExmRFYYJjxVRDdaTwMiQTEUOj0GAScIDCdBMTBMMARPTgEaMSYVKS0D/vsJDyxFNzlUMQNOfANNRjA8FgY/+gZBPCs6DQAAAQAc//MCUQKrAC0AAAUiJiYnIzczJjQ3IzczPgIzMhYXByYmIyIGByEHIwYXMwcjHgIzMjY3FwYGAWtHaD8MVRQ9AgJRFEIOTGo7R3cfMBRVRk5sEAEMFPwEBNEUtws0UTZIVxYvH3oNQXRNJxkxGSdWdDtOSxI0R2duJzEyJztgN0cxEkxKAAEAZ//0AisCqQA+AAAXJz4DNTQmJyM1MyYmNTQ2NjMyFhcHJiYjIgYGFRQWFzMVIxYWFRQGBzY2MzIWFjMyNjcXBgYjIiYmIyIGexIOKy0eCw1uWxMZMVc6U2oOPA5JNR1DLx0S2MYOBycmCxMIHzpBKBQmJQ8oMhEmPTwnHzoKJwcgMD4kFzsiJytEIzVKJ0s+DDcuEjEwI0crJyYyEzlKGAUCFBQKDSsQCxMUEQAAAQA1AAACQwKcABcAACE1IzUzNSM1MwMzEzMTMwMzFSMVMxUjFQEfoqKim+M8ygTJO+KZn5+fXCdiJwGQ/poBZv5wJ2InXAD//wEIANMBcAE7AgcA6gAAAN8AAQAuAAACSgKcAAMAADMBMwEuAdRI/iwCnP1kAAEARAAAAjQB8AALAAAhNSM1MzUzFTMVIxUBI9/fMt/f3zLf3zLfAAABAEQA4QI0ARMAAwAANzUhFUQB8OEyMgABAHEALQIHAcMACwAANyc3JzcXNxcHFwcnlCOoqCOoqCOoqCOoLSOoqCOoqCOoqCOoAAMARAAcAjQB1QADAA8AGwAANzUhFQciJjU0NjMyFhUUBgMiJjU0NjMyFhUUBkQB8PgYISEYGCAgGBghIRgYICDfMjLDIRgXISEXGCEBSB8YGCIiGBgfAAACAEQAhwI0AW0AAwAHAAATNSEVBTUhFUQB8P4QAfABOzIytDIyAAEARAAAAjQB8AATAAAzNyM1MzchNSE3MwczFSMHIRUhB29fiq1b/vgBK1w9XIirWwEG/tdehzKCMoODMoIyhwABAFUACQIjAeYABgAANzUlJTUFFVUBh/55Ac4JPLKzPNYxAAABAFUACQIjAeYABgAAJSU1JRUFBQIj/jIBzv55AYcJ1jHWPLOyAAIAWgAAAh4B4wAGAAoAADc1JSU1BRUBNSEVWgF2/ooBxP48AcRkPIKEPacy/vY1NQAAAgBaAAACHgHjAAYACgAAJSU1JRUNAjUhFQIe/jwBxP6KAXb+PAHEZKYypz2EgqA1NQACAE4AAAIqAfAACwAPAAAlNSM1MzUzFTMVIxUFNSEVASPV1TLV1f75AdxkrTKtrTKtZDU1AAACAEsAZwItAaAAFwAvAAA3JzY2MzIeAjMyNjcXBgYjIi4CIyIGJyc2NjMyHgIzMjY3FwYGIyIuAiMiBnwxBzo6Hj8+OxkhIgQxBzo6Hj8+OxkhIgQxBzo6Hj8+OxkhIgQxBzo6Hj8+OxkhImcKMT4UGRQlIgoxPhQZFCWYCjE+FBkUJSIKMT4UGRQlAAEASABsAjABfgAFAAAlNSE1IREB9/5RAehs3TX+7gAAAQBKAMQCLAFDABcAADcnNjYzMh4CMzI2NxcGBiMiLgIjIgZ7MQc6Oh4/PjsZISIEMQc6Oh4/PjsZISLECjE+FBkUJSIKMT4UGRQlAAEAZwE3AhECnAAGAAATEzMTIwMDZ7o2ujqbmwE3AWX+mwEu/tIAAwANAG4CawGSABcAJQAzAAA3IiY1NDYzMhYXNjYzMhYVFAYjIiYnBgYnMjY2Ny4CIyIGFRQWITI2NTQmIyIGBgceApVDRUVDMk4nJ04yQ0VFQzJOJydOMhclLCEhLCUXJzAwAXUnMDAnFiYsISEsJm5TPz9TPSoqPVM/P1M9Kio9MQ8qKCgqDzonJzo6Jyc6DyooKCoPAAABAJr/VAHfAqoAJwAAFyImNTQ2MzIWFRQHPgI1Ez4CMzIWFRQGIyImNTQ3IgYGBwMUBgbkIycZFRYaAwgSDwQBIDQeIycZFRYaAwcTDgEEIDSsHxYSGhoWCAgBDiopAiFMTBofFhIaGhYJBw8qKf3fS00aAAEAW/9eAh0CnAAHAAAXESERIxEhEVsBwjn+r6IDPvzCAwD9AAAAAQBa/14CHgKcAAsAABc1EwM1IRUhEwMhFVr6+gHE/n34+AGDojgBZwFnODj+mf6ZOAABAEb/XgJaApwACAAABQMjNTMTEzMDASiLV4CD1jvuogFPNP7EAvf8wgACAFf/9AIhAqgAHQAtAAAFIi4CNTQ+AjMyFhcuAwc1Mh4DFRQOAicyNjY1NCYmIyIGBhUUFhYBQCdRRispQ1AmPl4TCERhai1liVQsDy9ITCIoTzU1TygnUDU1UAwdPWJGRWE8HDo1SmE1EwMzMFJpdDlacTsWMSdbTUxaKChaTE1bJwAFABD/8gJoAqoADwAbAB8ALwA7AAATIiYmNTQ2NjMyFhYVFAYGJzI2NTQmIyIGFRQWAwEzAQUiJiY1NDY2MzIWFhUUBgYnMjY1NCYjIgYVFBamK0MoKEMrK0MoKEMrKjs7Kio7OxIBbzX+kQEzKkQoKEQqK0MoKEMrKjs7Kio7OwF8KEUqKkQpKUQqKkUoMDwrKzw8Kys8/lQCnP1kDihFKipFKChFKipFKDA8Kys8PCsrPAAHABz/8gJcAqoADwAbACsANwBHAFMAVwAAEyImJjU0NjYzMhYWFRQGBicyNjU0JiMiBhUUFhMiJiY1NDY2MzIWFhUUBgYnMjY1NCYjIgYVFBYFIiYmNTQ2NjMyFhYVFAYGJzI2NTQmIyIGFRQWJTUlFaQlPiUlPiUmPSUlPSYnNTUnJzU1JyU+JSU+JSY9JSU9Jic1NScnNTUBVyU+JSU+JSY9JSU9Jic1NScnNTX+dAI2AZwkPiUmPSQkPSYlPiQsNiUmNTUmJTb+KiQ+JSY9JCQ9JiU+JCw2JSY1NSYlNiwkPiUmPSQkPSYlPiQsNiUmNTUmJTbqMMswAAACAGv/wQINArgAAwAHAAAFAxMTAxMDAwE90tLQ0JSUlj8BewF8/oT+9wEJAQr+9gACAMACVwG4Aq8ACwAXAAABIiY1NDYzMhYVFAYjIiY1NDYzMhYVFAYBjBIaGhISGhqyEhoaEhIaGgJXGhISGhoSEhoaEhIaGhISGgD//wDAAuEBuAM5AgcBQAAAAIoAAQEPAlUBaQKvAAsAAAEiJjU0NjMyFhUUBgE8EhsbEhIbGwJVGhMTGhoTExoAAAEBDwLhAWkDOwALAAABIiY1NDYzMhYVFAYBPBIbGxISGxsC4RoTExoaExMaAAABAQwCVwFsArcACwAAASImNTQ2MzIWFRQGATwTHR0TFBwcAlcdExQcHBQTHQAAAQD3AjkBggK4AAsAAAEnJiY1NDYzMhYXFwFUORQQDgwMEgtIAjkzExINCw8RDmAAAQDxAuMBhwNHAAsAAAEnJiY1NDYzMhYXFwFWOhgTEAoKEQ5TAuMiDg8NDAwLDUwAAQD3AjkBggK4AAsAABM3NjYzMhYVFAYHB/dICxIMDA4QFDkCOWAOEQ8LDRITMwAAAQDxAuMBhwNHAAsAABM3NjYzMhYVFAYHB/FTDhEKCw8TGDoC40wNCwwMDQ8OIgAAAgC6AjkBvwK3AAsAFwAAEzc2NjMyFhUUBgcHMzc2NjMyFhUUBgcHukcLEgwMCxAUOVRHCxIMDAsQFDkCOWAOEA0LDBMTNGAOEA0LDBMTNP//ALoC5QG/A2MCBwFJAAAArAABARMCHAFmAs4ACwAAATc2NjMyFhUUBgcHARMUBA4QChMFBicCHIEZGAsPBRERcQABAMACQAG4AqcACwAAEzc2NjMyFhcXIycHwFALFQ0MFQpQN0ZEAkBSCwoKC1I8PP//AMAC4wG4A0oCBwFMAAAAowABAMACOgG4AqEACQAAASInJzMXNzMHBgE9GBVQN0RGN1AVAjoVUjw8UhUA//8AwALeAbgDRQIHAU4AAACkAAEAyQI8Aa8CnwANAAABIiYnMxYWMzI2NzMGBgE8Lj0IKAUmICAlBigIPAI8OygVJSUVKDsA//8AyQLjAa8DRgIHAVAAAACnAAIA3QI9AZsC+wAPABsAAAEiJiY1NDY2MzIWFhUUBgYnMjY1NCYjIgYVFBYBPBorGhorGhsrGRkrGxcgIBcWISECPRorGhorGhorGhorGighFhcgIBcWIQACAN0ChQGbA0MADwAbAAABIiYmNTQ2NjMyFhYVFAYGJzI2NTQmIyIGFRQWATwaKxoaKxobKxkZKxsXICAXFiEhAoUaKxoaKxoaKxoaKxooIRYXICAXFiEAAQCxAkABxwKUABsAAAEiJiYjIgYHIzY2MzIWFjMyNjc2NjMyFhUUBgYBehcoJBIRGQIoCDEdGCUiExARBwULBgYKECICQBQUFBMtIxMUEAsIBwgLByAa//8AsQLpAccDPQIHAVQAAACpAAEAvQJfAbsCiwADAAATNTMVvf4CXyws//8AvQLyAbsDHgIHAVYAAACTAAEA/f8YAXv/zAAPAAAFJz4CNTQmJzcWFhUUBgYBGx4RJRsRFCsUEx0s6BkCDh8ZEyIMEhEjFyAuGQABAOj/TwGRABQAIAAABSImJzcWFjMyNjU0JiMiBiMiJjc3Fwc2NjMyFhYVFAYGATkTLREOFR0PHBQSFwsXCAsFBy4eIwoRBBUbDhInsQ4KGQoHHAoOEAYSCksONgICEBkODyUaAAABAPv/WAF9ABEAFQAABSImJjU0NjY3FwYGFRQWMzI2NxUGBgFNFyYVFSsfIygtHhUKEQcPFagSIRcZJyAPEQ0tGhYVBAMnBgMAAAEA+/9YAX0ACgAVAAAFIiYmNTQ2NxcOAhUUFjMyNjcVBgYBTRcmFS4iMhonFB8TCRIIDxWoEiEXJTIRCggbIBEXFAQDJwYD//8AwAJXAbgCrwAGAUAAAP//AQ8CVQFpAq8ABgFCAAD//wD3AjkBggK4AAYBRQAA//8A9wI5AYICuAAGAUcAAP//ALoCOQG/ArcABgFJAAD//wDAAkABuAKnAAYBTAAAAAEAuAJ0AcACpwADAAATNSEVuAEIAnQzMwD//wDAAjoBuAKhAAYBTgAA//8AyQI8Aa8CnwAGAVAAAP//AN0CPQGbAvsABgFSAAD//wCxAkABxwKUAAYBVAAA//8AvQJfAbsCiwAGAVYAAP//AOj/TwGRABQABgFZAAD//wD7/1gBfQARAAYBWgAAAAMAQP/zAjgCqwAPABoAJgAABSImJjU0NjYzMhYWFRQGBicyNjcBBgYVFBYWAwE2NjU0LgIjIgYBPE1xPj5xTU1xPj5xTSlGGv7WEhM0WlABKhETHjVJKihGDU+bcnOaT0+ac3KbTzAeIQG0I2NBbYQ7Ahr+TSNiQFJyRyEeAAEAeAAAAgoCnAANAAAzNTMRIzUyNjY3MxEzFXq0tjtQMgwjpjAB5iYQKSf9lDAAAAEAYgAAAigCqAAaAAAzNT4CNTQmIyIGByc2NjMyFhYVFA4CByEVYoWjTE1DN1gOOBFvVjpZNDtlfEIBeDFmmYFAOk09RBFIWCpROz94cWcvNAABAGX/9AImAqgAMQAABSImJic3FhYzMjY2NTQmJiMjNTMyNjY1NCYmIyIGByc+AjMyHgIVFAYHFhYVFAYGAUFKXC8HNgtOTTJPLTJTMhkZIkkzKEAlRlQKNQs1WT8oRzYfOkBLSUBoDDJOKQo2TSRCLDRCIDAXOzQnNBtMMw4pSi4VKz4pNlcPDGM+QFgsAAACAD8AAAI6ApwACgAOAAAhNSE1ATMRMxUjFSUhESMBjv6xAUY/dnb+rwEbBLI+Aaz+SDKy5AFyAAEAaP/0Ah8CnAAjAAAFIiYmJzcWFjMyNjY1NCYmIyIGBycTIRUhBzY2MzIWFhUUBgYBQEhZLwg1C0tQK0wvMUwnMEwXMykBW/7RHRpLLztgOUBlDDFNKww6SytSOj9QJiwlDQFQNOshIjdmSExnNAACAGf/9AIpAqgAHgAqAAAFIiYmNTQ+AjMyFhcHJiYjIgYGBzY2MzIWFhUUBgYnMjY1NCYjIgYVFBYBS0pmNBg4X0ZFWRM0D0QuPFMtAxRhRz9fNjVjRUtbW0pNWloMR4dhSotvQT8rFCMrVI5YM0Q5ZUQ5ZkAwYkxQY2RPTGIAAAEAZgAAAhoCnAAGAAAzASE1IRUBvQEm/oMBtP7fAmg0NP2YAAMAUv/1AiYCqwAbACsAOwAABSImJjU0NjcmJjU0NjYzMhYWFRQGBxYWFRQGBicyNjY1NCYmIyIGBhUUFhYTMjY2NTQmJiMiBgYVFBYWATxGaTtJPiMqL1AxMVAvKiM+STtpRjdQLCxQNzZRLCxRNiM5IiI5IyM5IiI5CzVZNj1gFxdLLTFPLy9PMS1LFxdgPTZZNTAoQykpQygoQykpQygBWCI5IyM5IiI5IyM5IgAAAgBJAAACIwKrABQAJAAAMzcGBi4CNTQ2NjMyFhYVFAYGBwMDMjY2NTQmJiMiBgYVFBYW/ZInWFZHKkBrQkJsPxAdFKcFM1MxMVMzM1MyMlPlEwIgPVk4Qmw/P2xCJDg0IP7yAQYyUzMzUzIyUzMzUzIAAwBA//MCOAKrAA8AIQAtAAAFIiYmNTQ2NjMyFhYVFAYGJzI2NjU0LgIjIg4CFRQWFjciJjU0NjMyFhUUBgE8TXE+PnFNTXE+PnFNOVk0HjVJKipJNR40WjgWHh4WFh4eDU+bcnOaT0+ac3KbTzA7hG1SckchIUdyUm2EO/geFhYeHhYWHgABADUAAAJDApwAAwAAMwEzATUB1Dr+LAKc/WQAAwA/AAACPAKcAAgADAAkAAATNSM1MjY3MxEDATMBMzU+AjU0JiMiBgcnNjYzMhYVFAYHMxWDRBoqBytnAW81/pHbOEcjGxcTIAQwCTcqLDZLQpYBbeUgExf+0f6TApz9ZCgtPTAZFxoXGg4jKC4mLlIyLgAEAFsAAAJGApwACAAMABcAGwAAEzUjNTI2NzMRAwEzASE1IzU3MxUzFSMVJzM1I59EGioHK2cBbzX+kQFFnJwxMTGXZgIBbeUgExf+0f6TApz9ZEEsw8ItQW59AAQAIwAAAlUCqAADAA4AEgA7AAAzATMBITUjNTczFTMVIxUnMzUjJSImJzcWFjMyNjU0Jgc1FjY1NCYjIgYHJzY2MzIWFhUUBgcWFhUUBgZ5AW81/pEBRZycMTExl2YC/qAsOAoxBR8aGSIpMSwnHxQXHggvCzkrGC4dGRMaGR8yApz9ZEEsw8ItQW59hSgfDhQZGhcdGAIrAxoXFhUWFQ4gJRAkHRYjCgonFyApEwAAAQEGAWwBkAMLAAgAAAERIzUyNjczEQFgWh82CisBbAFQFh4b/mEAAAEArgFsAcsDEgAXAAATNT4CNTQmIyIGByc2NjMyFhUUBgczFa5KYS8qIRkwBTQIRDk4RWpn3wFsKT9eTiUgJR4rCC86PS88hE8rAAABAK8BZQHKAxIAKgAAASImJzcWFjMyNjU0JiYHNRY2NjU0JiMiBgcnNjYzMhYWFRQGBxYWFRQGBgE6PUQKLwgqKSQ7KT4hIDklMh4mLAkvC0o5GjwqIxwlIihCAWU9KgkcLCgnKCgLBCcDCSQiJR8iIwksOBUxKRwwEA85HCs4GwA=) format("truetype")}#__docusaurus-base-url-issue-banner-container,.call-to-action .call-to-action-icon,.docSidebarContainer_YfHR,.footer.footer--dark,.hide-desktop,.homepage .navbar__search,.sidebarLogo_F_0z,.themedComponent_mlkZ,.toggleIcon_g3eP,html[data-announcement-bar-initially-dismissed=true] .announcementBar_mb4j{display:none}.ds-dropdown-menu{background-color:var(--bg-color-code)}.navbar__search-input{font-size:var(--fontSizes-s);width:90%}.container .col article{padding:1rem 1rem 1rem 2rem}.theme-doc-sidebar-item-link-level-2{font-size:89%}body:not(.homepage) .main-wrapper{margin:0 auto;max-width:92rem;width:100%}.theme-doc-sidebar-menu{font-size:var(--fontSizes-s);font-weight:500}.menu__list-item .menu__link{padding-left:60px}.menu__list .navbar__item{display:block;margin-left:60px;margin-top:20px;padding-left:0}.menu__list-item-collapsible{font-weight:600}.menu__link--sublist-caret:after{background:var(--ifm-menu-link-sublist-icon) 50%/1.5rem 1.5rem}.footer{border-color:#fff;border-top:solid #fff;border-width:1px}.button,.button.light{border-style:solid;border-width:1px}.card{background-color:var(--ifm-navbar-background-color)}article .img-padding{background-color:#fff;border-radius:4px;padding:5px}article .img-in-text-right{background-color:#fff;border-radius:4px;float:right;margin:8px 8px 15px 30px}article .img-radius{background-color:#fff;border-radius:10px}.dropdown-content-sync-integration,.left{float:left}.right{float:right}.third{width:calc(33.33% - 20px)}.fifth,.padding-side-10-12,.third{padding-left:10px;padding-right:10px}.fifth{width:calc(20% - 20px)}.block h4,.full-width{width:100%}.centered{align-items:center;display:flex;flex-direction:column;justify-content:center}.pseudo-hidden{display:block;height:1px;overflow:hidden;width:1px}.underline{position:relative;z-index:5}h1 .underline{font-weight:400}.block h1 b,.block h2 b,.markdown h1{font-weight:800}.underline:after{background:var(--color-top);bottom:0;content:"";height:30%;left:0;opacity:.4;position:absolute;width:100%;z-index:-1}.block.dark,.block.fifth .box,.dark{background-color:var(--bg-color-dark)}.button{background-color:var(--color-top);border-color:var(--color-top);border-radius:8px;box-sizing:initial;color:#fff;cursor:pointer;font-size:1rem;font-weight:700;line-height:1.2;margin-right:20px;min-width:3rem;outline:#0000 solid 2px;outline-offset:2px;padding:10px 18px;text-align:center;transition:.15s ease-in-out;-webkit-user-select:none;user-select:none;will-change:box-shadow text-decoration transform}.navbar-icon,.price-calculator #price-calculator-submit{margin-right:0}.button.button-empty{background-color:unset;border-color:#fff;border-style:solid;cursor:pointer}.button.button-empty:hover{color:#fff}.button.light{background-color:var(--bg-color);border-color:var(--color-top)}#price-calculator-result a,.block.sixth a,.button.light:hover,.footer-policy div a:hover,.observe-code-example-tabs .ant-tabs-tab-btn,.package a,.trophy.twitter,footer a:visited{color:#fff}.block h1,.block h2,h2{font-size:var(--fontSizes-5xl)}.block h1,.block h2{letter-spacing:1.5px;word-spacing:-4px}h3{font-size:var(--fontSizes-xl)}.block.first.hero h1 b,.footer-nav-links a:hover,.pagination-nav a:hover,b{color:var(--color-top)}p{margin:0 0 var(--ifm-paragraph-margin-bottom);font-size:var(--fontSizes-m)}.markdown h1{color:#fff;font-size:var(--fontSizes-3xl)}.markdown h2{color:#fff;font-size:var(--fontSizes-xl);font-weight:700}.markdown h3{font-size:1.25rem;font-weight:600;margin-bottom:20px}.breadcrumbs__item--active .breadcrumbs__link,.call-to-action a b,.markdown b,.pagination-nav a,.table-of-contents__link,body,p b{color:var(--fontColor-offwhite)}.markdown a,.underline-link,p a,td a{color:#fff;font-weight:600;text-decoration-color:var(--color-top)!important;text-decoration-thickness:10%;text-underline-offset:4px;text-underline-position:from-font}.markdown a:hover,p a:hover{color:var(--color-top);text-decoration-color:var(--color-top)}body{font-weight:500}.table-of-contents{font-size:var(--fontSizes-xs)}.table-of-contents__link--active{color:var(--color-top);font-weight:500}.navbar{align-items:flex-start;border-bottom:1px solid var(--ifm-toc-border-color);display:flex;gap:10px;height:auto;justify-content:center;padding:16px 32px;z-index:10}.navbar .navbar__inner{align-items:center;display:flex;justify-content:space-between;padding:10px;width:1216px}.navbar .navbar__items--right{align-items:center;flex-direction:row-reverse;gap:20px;justify-content:center;margin-left:10%;padding:0 16px}.navbar__logo{aspect-ratio:106.93/38;height:38px}.navbar__items a{color:var(--fontColor-white);font-weight:500}.navbar__link:hover{color:#fff;text-decoration-color:var(--color-top);text-decoration-thickness:1.25px;text-underline-offset:4px}.navbar .navbar__brand{margin-left:10px;margin-right:28%}.navbar .navbar__brand b{font-size:var(--fontSizes-xl);font-weight:600;letter-spacing:-.04rem}.navbar-icon{height:30px;margin-left:8px;opacity:.9;padding:0;width:30px}.navbar__toggle{order:2;position:absolute;right:10px}.navbar-icon:hover,.star-at-github:hover{transform:scale(1.1)}.beating-first.animation,.beating-second.animation,.beating.animation{transform-origin:center center;will-change:transform}.navbar-icon-discord{background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyMyAxOSI+PGcgY2xpcC1wYXRoPSJ1cmwoI2EpIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTkuNSAyLjNBMTkgMTkgMCAwIDAgMTQuNyAxbC0uNiAxcS0yLjYtLjQtNS4yIDBMOC4zLjlxLTIuNS4zLTQuOCAxLjRjLTMgNC41LTMuOCA4LjgtMy40IDEzcTIuNyAyLjEgNS44IDMgLjgtMSAxLjMtMmwtMi0xcS4zIDAgLjUtLjNhMTQgMTQgMCAwIDAgMTEuNiAwbC41LjQtMiAuOSAxLjMgMnEzLS45IDUuOC0zIC42LTcuMy0zLjQtMTNNNy43IDEyLjdjLTEuMiAwLTItMS0yLTIuMlM2LjQgOCA3LjYgOHExLjguMiAyIDIuNGMwIDEuMi0uOSAyLjItMiAyLjJtNy42IDBxLTEuOC0uMi0yLTIuMmMwLTEuMy45LTIuNCAyLTIuNHEyIC4yIDIgMi40YzAgMS4yLS44IDIuMi0yIDIuMiIvPjwvZz48ZGVmcz48Y2xpcFBhdGggaWQ9ImEiPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0wIC45aDIzdjE3LjRIMHoiLz48L2NsaXBQYXRoPjwvZGVmcz48L3N2Zz4=) 50%/contain no-repeat}.navbar-icon-github{background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMTUgM2ExMiAxMiAwIDAgMC0zIDIzLjZWMjRoLTEuNXEtMS4zIDAtMi0xYy0uMy0uNy0uNC0xLjktMS4zLTIuNXEtLjMtLjUuMi0uNS45LjMgMS42IDEuMmMuNS43LjcuOCAxLjYuOGwxLjctLjFxLjUtMS40IDEuNi0yYy00LS40LTYtMi40LTYtNVE4IDEzIDkuNCAxMS41Yy0uMi0xLS42LTIuOS4xLTMuNiAxLjggMCAzIDEuMiAzLjIgMS41YTkgOSAwIDAgMSA1LjggMGMuMy0uMyAxLjQtMS41IDMuMi0xLjUuNy43LjMgMi43IDAgMy42UTIzIDEzIDIzIDE0LjhjMCAyLjctMiA0LjctNS45IDUuMSAxLjEuNiAxLjkgMi4yIDEuOSAzLjR2M0ExMiAxMiAwIDAgMCAxNSAzIiBmb250LWZhbWlseT0ibm9uZSIgZm9udC1zaXplPSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOm5vcm1hbCIgdGV4dC1hbmNob3I9Im5vbmUiIHRyYW5zZm9ybT0ic2NhbGUoOSkiLz48L3N2Zz4=) 50%/contain no-repeat}.header-space{height:calc(var(--space-between-blocks) + 110px);width:100%}.content{margin-left:auto;margin-right:auto;max-width:min(1120px,94%);width:100%}.slick-slider{padding-bottom:50px}.slider-content{background:var(--bg-color-dark);display:flex;flex-direction:column;padding:16px}.slider-content h3{font-size:1.2em;font-weight:400;line-height:28px}.slider-content .slider-profile .slider-info{margin-left:16px}.slider-content .slider-profile .developer b{color:#fff;font-weight:800}.device{align-items:center;background-repeat:no-repeat;display:flex;flex-direction:column}.device .beating-color{background-color:#ed168f;transition:background-color .1s linear}.device.desktop .beating-color{align-items:center;border-radius:9% 9% 0 0/19% 19% 0 0;display:flex;height:53.1%;justify-content:center;margin-left:auto;margin-right:auto;margin-top:2.57%;width:95.1%}.device.desktop .beating.logo{width:42%}.device.server{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiIHZlcnNpb249IjEuMiIgdmlld0JveD0iMCAwLjQgNjAgNTkuMSI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNLjggMjEuNWg1OC40Yy41LjcuOCAxLjYuOCAyLjZ2MTEuOGMwIDEtLjMgMS45LS44IDIuNkguOGMtLjUtLjctLjgtMS42LS44LTIuNlYyNC4xYzAtMSAuMy0xLjkuOC0yLjZtNTAuMiAxMGMwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFNNiAzMGMwIDIuNSAyIDQuNSA0LjUgNC41czQuNS0yIDQuNS00LjUtMi00LjUtNC41LTQuNVM2IDI3LjUgNiAzME0uOCAxOS41Yy0uNS0uNy0uOC0xLjYtLjgtMi42VjUuMUMwIDIuNiAyLjEuNSA0LjYuNWg1MC44QzU3LjkuNSA2MCAyLjYgNjAgNS4xdjExLjhjMCAxLS4zIDEuOS0uOCAyLjZ6bTUwLjItN2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTIgM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFtLTItM2MwIC42LjQgMSAxIDFzMS0uNCAxLTEtLjQtMS0xLTEtMSAuNC0xIDFNNiAxMWMwIDIuNSAyIDQuNSA0LjUgNC41czQuNS0yIDQuNS00LjUtMi00LjUtNC41LTQuNVM2IDguNSA2IDExTTU5LjIgNDAuNWMuNS43LjggMS42LjggMi42djExLjhjMCAyLjUtMi4xIDQuNi00LjYgNC42SDQuNmMtMi41IDAtNC42LTIuMS00LjYtNC42VjQzLjFjMC0xIC4zLTEuOS44LTIuNnpNMTUuMSA0OWMwLTIuNS0yLTQuNS00LjUtNC41cy00LjUgMi00LjUgNC41IDIgNC41IDQuNSA0LjUgNC41LTIgNC41LTQuNW0yMC0xLjVjMC0uNi0uNS0xLTEtMS0uNiAwLTEgLjQtMSAxcy40IDEgMSAxYy41IDAgMS0uNCAxLTFtMiAzYzAtLjYtLjUtMS0xLTEtLjYgMC0xIC40LTEgMXMuNCAxIDEgMWMuNSAwIDEtLjQgMS0xbTItM2MwLS42LS41LTEtMS0xLS42IDAtMSAuNC0xIDFzLjQgMSAxIDFjLjUgMCAxLS40IDEtMW0yIDNjMC0uNi0uNS0xLTEtMS0uNiAwLTEgLjQtMSAxcy40IDEgMSAxYy41IDAgMS0uNCAxLTFtMi0zYzAtLjYtLjUtMS0xLTEtLjYgMC0xIC40LTEgMXMuNCAxIDEgMWMuNSAwIDEtLjQgMS0xbTIgM2MwLS42LS41LTEtMS0xLS42IDAtMSAuNC0xIDFzLjQgMSAxIDFjLjUgMCAxLS40IDEtMW0yLTNjMC0uNi0uNS0xLTEtMS0uNiAwLTEgLjQtMSAxcy40IDEgMSAxYy41IDAgMS0uNCAxLTFtMiAzYzAtLjYtLjUtMS0xLTEtLjYgMC0xIC40LTEgMXMuNCAxIDEgMWMuNSAwIDEtLjQgMS0xbTItM2MwLS42LS41LTEtMS0xLS42IDAtMSAuNC0xIDFzLjQgMSAxIDFjLjUgMCAxLS40IDEtMW0yIDNjMC0uNi0uNS0xLTEtMS0uNiAwLTEgLjQtMSAxcy40IDEgMSAxYy41IDAgMS0uNCAxLTEiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJtMyAxMCA2IDcgNiAxIDMtNmMuNC0xLjItMi03LTItNy0xLjEtLjUtNS0xLTUtMUw1IDZtLTIgNCA2IDcgNiAxIDMtNmMuNC0xLjItMi03LTItNy0xLjEtLjUtNS0xLTUtMUw1IDZNMiA0OWw2IDcgNiAxIDMtNmMuNC0xLjItMi03LTItNy0xLjEtLjUtNS0xLTUtMWwtNiAybS0yIDQgNiA3IDYgMSAzLTZjLjQtMS4yLTItNy0yLTctMS4xLS41LTUtMS01LTFsLTYgMk0zIDI5bDYgNyA2IDEgMy02Yy40LTEuMi0yLTctMi03LTEuMS0uNS01LTEtNS0xbC02IDJtLTIgNCA2IDcgNiAxIDMtNmMuNC0xLjItMi03LTItNy0xLjEtLjUtNS0xLTUtMWwtNiAyIi8+PC9zdmc+);bottom:0;height:45%;width:23%}.dark .neumorphism-circle-m,.dark .neumorphism-circle-s,.slider-content .slider-profile img.slider-logo-black{background:var(--bg-color-dark)}.device.server .beating-color{border-radius:50%;height:18px;left:17%;margin-left:-9px;margin-top:-9px;position:absolute;width:18px}.device.server .beating-color.one{top:17%}.device.server .beating-color.two{top:50%}.device.server .beating-color.three{top:84%}.block{background-color:var(--bg-color);min-height:250px;overflow:hidden;padding-bottom:46px;padding-top:50px;position:relative;width:100%}.block.trophy-before{padding-top:120px}.block.trophy-after{padding-bottom:80px}.block.first .button{float:left;margin-bottom:10px;margin-left:3px;margin-right:5px;min-width:100px}.block .inner{display:flex;gap:20px;justify-content:center;overflow:hidden}.justify-center-mobile{justify-content:right}.grid-3{grid-template-columns:auto auto auto}.framework-icon{flex-shrink:0;height:40px;width:40px}.font-20-14,.font-20-16{font-size:20px}.font-16-14{font-size:16px}.gap-24-20{gap:24px}.height-26-21{height:26px}.margin-left-10-0,.observe-code-example-tabs .ant-tabs-tab{margin-left:10px}.margin-right-8{margin-right:8px}.margin-right-6-8{margin-right:6px}.padding-top-64-46{padding-top:64px}.padding-side-16-12{padding-left:16px;padding-right:16px}.width-140-120{width:140px}.margin-bottom-16-10{margin-bottom:16px}.margin-bottom-12{margin-bottom:12px}.margin-right-10-6{margin-right:10px}.padding-right-20-0{padding-right:20px}.font-30-20{font-size:30px}.gap-30-16{gap:30px}.min-width-180-135{min-width:180px}.flex-end-center{justify-content:end}.flex-start-center{justify-content:start}.margin-top-125-0{margin-top:125px}.padding-button{padding:6px 25px}.block .half.left{flex:0.9}.block .half.right{flex:1.12}.block .centered .inner{display:block;justify-content:center;overflow:hidden}.block.first.hero h1{margin-left:auto;margin-right:auto;width:88%}.block.first.hero .text{color:#b5b5b5;font-size:var(--fontSizes-l);font-weight:400;letter-spacing:-.05rem;line-height:150%;margin-bottom:0;text-align:left;white-space:pre-line;width:80%}.block.first.hero .hero-action{flex:1 1 auto;max-width:300px;text-align:center}.block.first.hero .buy-premium-hero{display:inline-block;margin-left:20px;margin-top:7px}.block.reviews p{max-width:900px}.block.reviews .inner{max-width:1224px}.block.second .inner{display:flex;margin-top:10px}.content-canvas{aspect-ratio:2/1;max-width:540px;transform:scale(1);width:100%}.block.replication .replication-icons{height:515px;margin-left:10%;margin-top:0;position:relative;width:90%}.block.replication .replicate-logo{left:-60px;margin-left:50%;margin-top:-75px;position:absolute;top:50%;width:120px}.block.replication .replicate-graphql{margin-left:10%;margin-top:8%;position:absolute}.block.replication .replicate-firestore{margin-left:2%;margin-top:-47.5px;position:absolute;top:51%}.block.replication .replicate-supabase{margin-left:81%;margin-top:-47.5px;position:absolute;top:51%}.block.replication .replicate-rest{left:-35px;margin-left:20%;margin-top:-70px;position:absolute;text-align:center;top:83%}.block.replication .replicate-websocket{left:-35px;margin-left:50%;margin-top:-70px;position:absolute;text-align:center;top:93%}.block.replication .replicate-webrtc{left:80%;margin-left:-35px;margin-top:-70px;position:absolute;text-align:center;top:83%}.block.replication img.protocol{width:80px}.block.replication .neumorphism-circle-s img.protocol{width:40px}.block.offline-first{overflow:hidden;position:relative}.block.offline-first .offline-image-wrapper{left:46%;margin-left:-750px;position:absolute;top:-12%;transform:rotate(77deg);width:1500px}.block.offline-first p{margin-bottom:32px}.block.offline-first .offline-image{height:100%;transform-origin:center bottom;width:100%}ul.checked li{line-height:116%;padding-top:20px;text-align:left}ul.checked li,ul.checked li b{font-size:26px}ul.checked li:before{font-size:45px}.block.frameworks .content{position:relative}.block.frameworks h2{margin-top:50px}.block.frameworks p{padding-bottom:23px}.block.frameworks .circle{font-size:85%;position:absolute}.block.frameworks .circle img{height:46%;padding-bottom:3px}.block.frameworks .neumorphism-circle-s{margin-left:-35px}.block.frameworks .neumorphism-circle-m{margin-left:-47.5px}.block.frameworks .below-text{display:block;min-height:200px;position:relative;width:100%}.block.fifth .inner{width:624px}.block.fifth .box{border-radius:.75rem;box-sizing:initial;float:left;margin-right:20px;margin-top:20px;padding:12px 16px;width:calc(50% - 72px)}.block table,.block.fifth.dark .box,.block.sixth .buy-option-inner{background-color:var(--bg-color)}.block.fifth .box img{box-sizing:initial;float:left;margin-top:4px;padding-right:10px;width:20px}.block.fifth .box .label{float:left;margin-top:2px}.block.fifth .box .value{color:var(--color-top);float:right;font-weight:700;margin-inline-end:0;margin-top:2px;right:0}.block.sixth .content{box-sizing:initial}.block.sixth .buy-options{align-items:center;column-gap:10px;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));row-gap:1em;width:100%}.block.sixth .buy-option{border-radius:6px;padding:3px}.block.sixth .buy-option-inner{border-radius:4px;box-sizing:initial;color:#fff;padding:10px;position:relative}.block.sixth .buy-option-inner h2{font-size:1.5em;height:50px;text-align:center;width:100%}.block.sixth .buy-option-title{padding-bottom:35px}.block.sixth .buy-option-inner .price{font-size:.8em;text-align:center;width:100%}.block.sixth .buy-option-features{margin-left:5%;width:90%}.block.sixth .buy-option-features p{font-size:1em;text-align:justify}.block.sixth .buy-option-action{border-radius:6px;bottom:8px;color:#fff;cursor:pointer;font-size:1.2rem;font-weight:700;left:10px;padding-bottom:15px;padding-top:15px;position:absolute;text-align:center;width:calc(100% - 20px)}.block.sixth .buy-option-features{font-size:1em;padding-bottom:50px}.block.sixth .buy-option-features li{padding-bottom:10px}.block.last .buttons{height:382px;margin-top:66px;position:relative}.block.last .buttons .button{display:inline;left:0;margin:0;position:absolute;top:0}.block.last .button.get-premium{border-radius:10px;font-size:37px;padding:10px 38px}.block table{border:1px solid #ffffff4d;border-radius:6px;font-size:120%;margin-top:20px;padding:0;width:80%}.neumorphism-circle-xl{box-shadow:5px 5px 10px #161c26,-5px -5px 10px #1e2432;height:130px;width:130px}.neumorphism-circle-m,.neumorphism-circle-xl{background:var(--bg-color);border-radius:50%;color:#fff;font-size:85%}.neumorphism-circle-m{box-shadow:5px 5px 10px #171c26,-5px -5px 10px #1d2432;height:95px;width:95px}.neumorphism-circle-s,.neumorphism-circle-xs{background:var(--bg-color);border-radius:50%;color:#fff;font-size:85%;height:70px;width:70px}.dark .neumorphism-circle-m{box-shadow:5px 5px 10px #14161e,-5px -5px 10px #1a1c28}.neumorphism-circle-s{box-shadow:3px 3px 6px #151a24,-3px -3px 6px #1f2634}.dark .neumorphism-circle-s{box-shadow:3px 3px 6px #13151d,-3px -3px 6px #1b1e29}.neumorphism-circle-xs{box-shadow:5px 5px 7px #171c26,-5px -5px 7px #1d2432}.trophy,.trophy.github{color:#eac54f}.bg-top{background-color:var(--color-top)}.bg-middle{background-color:var(--color-middle)}.bg-bottom{background-color:var(--color-bottom)}.hover-shadow-top:hover{box-shadow:2px 2px 6px var(--color-top),-2px -1px 10px var(--color-top)}.hover-shadow-middle:hover{box-shadow:2px 2px 10px var(--color-middle),-2px -1px 10px var(--color-middle)}.hover-shadow-bottom:hover{box-shadow:2px 2px 10px var(--color-bottom),-2px -1px 10px var(--color-bottom)}.bg-gradient-right-bottom{background:linear-gradient(to right bottom,var(--color-top),var(--color-middle),var(--color-bottom))}.bg-gradient-left-bottom{background:linear-gradient(to left bottom,var(--color-top),var(--color-middle),var(--color-bottom))}.bg-gradient-right-top{background:linear-gradient(to right top,var(--color-top),var(--color-middle),var(--color-bottom))}.bg-gradient-left-top{background:linear-gradient(to left top,var(--color-top),var(--color-middle),var(--color-bottom))}.bg-gradient-top{background:linear-gradient(to top,var(--color-top),var(--color-middle),var(--color-bottom))}.star-at-github{border-radius:40px;float:right;margin-top:8px;padding:3px;transition:.2s ease-in-out}.star-at-github .star-at-github-inner{background-color:var(--bg-color);border-radius:40px;padding:8px}.star-at-github .star-at-github-inner img{margin-left:3px;margin-right:6px;margin-top:3px;width:17px}.star-at-github .star-at-github-text{display:inline;float:right;margin-top:2px}.tilt-to-mouse{box-shadow:0 0 0 1px #0000;image-rendering:optimizeQuality;outline:#0000 solid 1px}.call-to-action-popup,.dropdown-content,.trophy{box-shadow:0 8px 12px 6px #00000026,0 4px 4px 0 #0000004d;left:50%}.enlarge-on-mouse,.tilt-to-mouse{will-change:transform}.beating.animation{animation:a}.beating-first.animation{animation:b}.beating-second.animation{animation:c}.beating-color{background-color:#ed168f;transition:background-color .4s linear;will-change:background-color}.trophy{align-items:center;background-color:var(--bg-color);border-radius:10px;border-style:solid;border-width:2px;box-sizing:initial;display:flex;height:40px;margin-top:-26.715px;padding:6px 10px 4px;position:absolute;transform:translateX(-50%);width:220px;z-index:9}.samp-wrapper,.trophy.discord,.trophy.github,.trophy.mongodb,.trophy.supabase{background-color:var(--bg-color-dark)}.trophy:hover{box-shadow:2px 2px 13px #eac54f,-2px -1px 14px #eac54f}.trophy img,.trophy svg{align-items:flex-start;aspect-ratio:1/1;display:flex;flex-direction:column;height:30px;margin-right:10px;margin-top:-4px;width:30px}.trophy .subtitle{font-size:12px;line-height:normal}.trophy .subtitle,.trophy .title{box-sizing:initial;font-style:normal;font-weight:500}.trophy .title{font-size:16px;line-height:25px}.trophy .valuetitle{box-sizing:initial;font-size:12px;font-style:normal;font-weight:500;line-height:normal}.trophy .value{font-feature-settings:"tnum";font-variant-numeric:tabular-nums;line-height:25px}.trophy .arrow-up,.trophy .value{box-sizing:initial;font-size:16px;font-style:normal;font-weight:500}.trophy .arrow-up{float:right;line-height:normal;margin-left:5px;margin-top:2px}.trophy.twitter:hover{box-shadow:2px 2px 13px #fff,-2px -1px 14px #fff}.trophy.discord{color:#878ef2}.trophy.discord:hover{box-shadow:2px 2px 13px #878ef2,-2px -1px 14px #878ef2}.trophy.supabase{color:#3ecf8e}.trophy.supabase:hover{box-shadow:2px 2px 13px #3ecf8e,-2px -1px 14px #3ecf8e}.trophy.mongodb{color:#00a35c}.trophy.mongodb:hover{box-shadow:2px 2px 13px #00a35c,-2px -1px 14px #00a35c}.samp-wrapper{background-color:var(--bg-color);border-radius:15px;border-style:solid;border-width:1px;display:inline-block;font-size:16px;padding:12px;text-align:left;width:calc(100% - 26px)}.samp-wrapper legend{font-weight:700;padding-left:6px;padding-right:6px}samp{font-family:Courier New,monospace;line-height:157%}samp .beating-color{border-radius:5px;color:#fff!important;font-weight:700;padding:2px 4px}samp .cm-keyword,samp .cm-operator{color:#c678dd}samp .cm-variable{color:#e5c07b}samp .cm-html{color:#c5436d}samp .cm-def,samp .cm-property{color:#e06c75}samp .cm-method{color:#61afef}samp .cm-string{color:#98c379}samp .cm-comment{color:#7f848e}.observe-code-example-tabs{color:#fff;padding-bottom:20px}.ant-tabs-tab-btn,footer{color:#fff!important}.ant-tabs-tab-btn[aria-selected=true]{color:var(--color-top)!important}.ant-tabs-ink-bar{background:var(--color-top)!important}.observe-code-example-tabs .ant-tabs-tab-icon{margin-inline-end:5px!important}.observe-code-example-tabs .ant-tabs-tab-icon img{float:left;height:30px;margin-top:-6px}@keyframes a{0%,26%,76%,to{transform:scale(1)}13%{transform:scale(1.1)}16%{transform:scale(1.08)}22%{transform:scale(1.2)}38%{transform:scale(1.09)}41%,56%{transform:scale(1.05)}50%{transform:scale(1.07)}}@keyframes b{0%,26%,to{transform:scale(1)}13%{transform:scale(1.1)}16%{transform:scale(1.08)}22%{transform:scale(1.2)}}@keyframes c{0%,26%,76%,to{transform:scale(1)}38%{transform:scale(1.09)}41%,56%{transform:scale(1.05)}50%{transform:scale(1.07)}}.premium-blocks{display:grid;grid-auto-rows:1fr;grid-template-columns:repeat(4,1fr);margin-top:30px;width:80%;grid-column-gap:10px;grid-row-gap:15px}.navbar-line{top:89.5px}.premium-blocks .premium-block{border-radius:6px;color:#fff;height:100%;padding:3px}.premium-blocks .premium-block-inner{background-color:var(--bg-color);border-radius:4px;box-sizing:initial;color:#fff;height:calc(100% - 20px);padding:10px;position:relative}.premium-blocks p{font-size:93%}.price-calculator{background-color:var(--bg-color);border:1px #ffffff4d;font-size:120%;margin-top:20px;padding:3px;width:80%}.price-calculator-inner{margin-left:5%;padding-bottom:40px;padding-top:40px;width:90%}.package{border-radius:0;margin-bottom:10px;margin-top:10px;padding:3px}.package-inner{background-color:var(--bg-color-dark);border-color:var(--fontColor-gray);border-radius:16px;border-style:none;border-width:1px;padding:16px;position:relative}.price-calculator h4{margin-bottom:20px;margin-top:0}.price-calculator .field{font-size:80%;margin-bottom:15px;width:100%}.price-calculator .field label{font-size:120%;margin-bottom:5px;width:100%}.price-calculator .field .input{border-radius:4px;text-align:left;width:100%}.price-calculator .field .input input[type=number]{width:150px}.price-calculator .field .suffix{float:left;font-size:120%;margin-left:10px;margin-top:4px}.price-calculator .field .prefix{float:left;font-size:120%;margin-right:10px;margin-top:4px}.price-calculator input,.price-calculator select{background-color:#fff;border-radius:3px;color:#000;float:left;font-size:120%;padding:3px}.price-calculator hr{height:1px;margin-bottom:30px;margin-top:50px}.price-calculator .package-checkbox{accent-color:var(--color-middle);background-color:#fff;border-radius:5px;cursor:pointer;float:right;height:50px;width:50px}.price-calculator .package ul li{line-height:150%}.price-calculator input:invalid:required,.price-calculator select:invalid:required{border:2px solid red}#price-calculator-result{font-size:120%}#price-calculator-result .inner{text-align:center;width:100%}#price-calculator-result .price-label{font-size:100%;line-height:320%;vertical-align:top}#price-calculator-result #total-per-project-per-month{font-size:300%}#price-calculator-result .per-month{font-size:120%;line-height:300%}#price-calculator-result table{border-spacing:30px;width:100%}#price-calculator-result #total-per-year{border-bottom:3px double;font-weight:700}.premium-faq h2{padding-bottom:40px}.premium-faq details{font-size:var(--fontSizes-m);padding-bottom:25px;text-align:justify;width:60%}.premium-faq details[open]{padding-bottom:25px}.premium-faq summary{cursor:pointer;font-weight:700;padding-bottom:10px}.premium-request li,.premium-request ol{padding-bottom:20px}.premium-request iframe{border:#ffffff4d;border-radius:14px;height:2150px;margin:0;overflow:hidden;padding:0;width:700px}footer{font-weight:700;padding:20px;text-align:center}.redirectBox ul{display:inline-block;text-align:left}.redirectBox li{margin:10px 0}.call-to-action{align-items:center;display:flex;flex:1;min-width:0;overflow:hidden;padding-left:9px;padding-right:9px}.call-to-action a{background-color:var(--ifm-navbar-background-color);border:1px solid var(--color-top);border-radius:1rem;box-sizing:initial;cursor:pointer;height:2rem;line-height:2rem;padding-left:1rem;padding-right:1rem;text-align:center;transition:.2s}.call-to-action a,.call-to-action-text{color:var(--fontColor-offwhite);font-size:var(--fontSizes-xs)}.call-to-action-text,.tags_jXut{display:inline}.call-to-action-popup{background-color:#fff;background:var(--bg-color-dark);border:1px solid var(--40-grey,#666);bottom:0;margin-bottom:20px;max-width:90%;padding:16px;position:fixed;text-align:center;transform:translateX(-50%) translateY(200%);width:560px;will-change:transform;z-index:20}.ant-modal-content,.block.faq .ant-collapse{background-color:initial}.call-to-action-popup.active{transform:translateY(0) translateX(-50%);transition:.5s}.call-to-action-popup.active.top{bottom:unset;top:80px}.call-to-action-popup.active.mid{bottom:unset;margin-top:-100px;top:50%}.call-to-action-popup h3{font-size:20px;font-style:normal;font-weight:700;line-height:normal;margin-bottom:20px;margin-top:14px;width:calc(100% - 30px)}.call-to-action-popup .close-popup{align-items:center;cursor:pointer;display:flex;justify-content:center;overflow:hidden;padding:16px;position:absolute;right:0;text-align:center;top:0}.call-to-action-popup .close .text{margin-top:2px}#CybotCookiebotDialog{font-family:var(--ifm-font-family-monospace)!important;padding:12px!important}#CybotCookiebotDialogHeader,#CybotCookiebotDialogPoweredByText{display:none!important}.CybotCookiebotDialogBodyBottomWrapper{margin-top:.5em!important}#CybotCookiebotDialogBodyLevelButtonCustomize{border-color:var(--color-top)!important}.navbar__title{color:#fff;font-size:1.35rem;font-weight:500}.flex-row{align-items:center;flex-direction:row}.docMainContainer_TBSr,.docRoot_UBD9,.flex-column,.flex-row{display:flex;width:100%}.flex-column,.side-tabs.small{flex-direction:column}.ant-modal-body h3{margin-bottom:12px;width:calc(100% - 40px)}.slick-dots li,.slick-dots li button,.slick-dots li button:before{height:7px!important;width:7px!important}.nav-logo-consulting{align-items:center;color:#fff;display:flex;font-size:1.35em;font-weight:500;gap:9px;line-height:120%;margin-right:48px!important;padding:0!important;transition:.15s ease-in-out}.hero-img,.nav-logo-consulting:hover{max-width:fit-content}.block.review .slider-content h3{color:#f6f6f7;font-size:1.2em;font-weight:600;line-height:160%;margin-bottom:32px;margin-top:16px;text-align:start}.slider-content .slider-profile{align-items:center;display:flex;gap:0;margin-top:auto}.slick-track{display:flex!important;gap:32px}.slick-initialized .slick-slide{display:flex!important;flex-grow:1;height:auto;max-width:600px}.slider-content .slider-profile img{border-radius:100%;height:30px;object-fit:contain;padding:5px;width:30px}.review-img{max-height:24px;padding-bottom:5px}.slider-content .slider-profile img.slider-logo-white{background:#fff}.slider-content .slider-profile .slider-info{width:70%}.slider-content .slider-profile .developer{font-style:normal;color:#fff;font-size:1em;font-weight:500;line-height:150%;margin-bottom:0}.slider-content .slider-profile .company-link{color:#858585;font-size:1em;font-weight:400;line-height:150%}.slick-slide{display:flex;flex-grow:1;height:auto}.slick-next:before,.slick-prev:before{content:""!important}.slick-next,.slick-prev{top:40%!important}.slick-list{height:auto!important;-webkit-mask-image:-webkit-gradient(linear,left center,right center,color-stop(0,#0000),color-stop(.3,#000),color-stop(.7,#000),color-stop(1,#0000));mask-image:-webkit-gradient(linear,left center,right center,color-stop(0,#0000),color-stop(.3,#000),color-stop(.7,#000),color-stop(1,#0000));max-height:700px;padding:0}.slick-dots{align-items:center!important;border:1px solid #2c3039;border-radius:22px;display:flex!important;margin:40px auto 0!important;padding:8px!important;position:static!important;width:max-content!important}.slick-dots li{margin:0 8px!important;padding:0 0 0 7px!important;top:1px}.slick-dots li.slick-active{padding-right:15px!important;top:0}.slick-dots li button:before{color:#c3c3d0!important;font-size:10px!important;line-height:7px!important;opacity:1!important}.slick-dots li.slick-active button:before{border-radius:80px;content:"ㅤ"!important;height:8px!important;margin:0 7px 0 0!important;width:24px!important}.slick-dots li.slick-active:first-child button:before{margin:0 50px 0 0!important}.block.faq .ant-collapse{display:flex;flex-direction:column;gap:16px}.block.faq .ant-collapse-header{align-items:center;color:#fff;flex-direction:row-reverse;font-size:1.15em;font-weight:600;gap:16px;line-height:150%;padding:0}.block.faq .ant-collapse-item{background-color:#222834;border:none;border-radius:24px;color:#fff;font-size:1.15em;font-weight:600;line-height:150%;padding:26.5px 24px}.block.faq .ant-collapse-content-box{color:#fff;font-size:1em;font-weight:400;line-height:150%;padding:12px 0 0!important;white-space:pre-line}.block.footer{padding:25px 30px 60px}.block.footer .footer-block{display:flex;flex-direction:column;gap:2rem;max-width:90rem;width:100%}.block.footer .footer-links{color:#fff;display:flex;flex-direction:column;gap:1rem}.block.footer .footer-links span{align-items:center;display:flex;justify-content:space-between;padding:1rem 0}.footer-logo-button{align-items:center;color:#fff;display:flex;font-size:var(--fontSizes-2xl);font-weight:600;line-height:120%}.footer-policy div a,.footer-rights{color:#5c5c75;font-size:var(--fontSizes-s);font-weight:400;line-height:150%}.footer-logo-button img{max-height:54px}.footer-logo-button div{align-items:center;cursor:pointer;display:flex;gap:13px;padding:0 0 0 3px}.footer-links a{align-items:center;color:#fff;display:flex;font-size:var(--fontSizes-s);font-weight:400;transition:.15s ease-in-out}.footer-community-links{align-items:center;display:flex;gap:16px;margin-top:130px}.footer-community-links img,.footer-community-links svg{filter:grayscale(100%) brightness(1.8);height:22px}.footer-policy{align-items:center;border-top:1px solid #5c5c75;display:flex;justify-content:space-between;padding-top:1rem}.footer-policy div{display:flex;gap:1rem}.footer-policy div a{align-items:center;display:flex;transition:.15s ease-in-out}.footer-img{bottom:0;position:absolute;right:0}.tag-cloud{max-width:100%;overflow-x:hidden;text-align:center}.dropdown-wrapper{display:inline-block;position:relative}.dropdown-content{background:red;background:var(--bg-color-dark);border:1px solid #666;padding:32px;position:fixed;top:82px;transform:translateX(-50%);width:809px;z-index:13}.dropdown-content-storages{align-items:start;column-gap:32px;display:grid;grid-template-columns:repeat(4,max-content);padding:13px 16px 24px}.dropdown-grid-top{border-bottom:2px solid #666;padding-bottom:6px}.dropdown-grid-top-title{font-size:14px;font-style:normal;font-weight:700;line-height:21px}.dropdown-grid-top-sub{font-size:12px;font-style:normal;font-weight:500;line-height:normal;white-space:nowrap}.dropdown-grid-top-links{list-style-type:none;padding-left:14px}.dropdown-grid-top-links li{padding-top:14px}.dropdown-wrapper:hover .dropdown-content{display:grid}.dropdown-content-sync{padding:32px}.dropdown-content-sync-card{border:2px solid #fff;justify-content:center;margin-bottom:var(--ifm-paragraph-margin-bottom);padding:10px 14px 0}.dropdown-content-sync-card:hover{background-color:#000;filter:invert(1)}.dropdown-content-sync-title{font-size:20px;font-style:normal;font-weight:700;line-height:normal}.dropdown-content-sync-subtitle{font-size:12px;font-style:normal;font-weight:500;margin-top:10px}.dropdown-content-sync-integrations{display:grid;gap:10px 40px;grid-template-columns:repeat(4,1fr)}.side-tabs{display:flex;max-width:100%}.side-tabs__list{display:flex;flex:0 0 auto;flex-direction:column;gap:4px;min-width:220px}.side-tabs__tab{align-items:center;border-bottom:2px solid;border-left:2px solid;border-top:2px solid;border-color:var(--bg-color-dark);cursor:pointer;display:flex;font-size:20px;font-weight:500;gap:0;height:52px;text-align:left}.side-tabs__tab.dark{border-color:var(--bg-color)}.alert.alert--info,.alert.alert--info div div{border-color:var(--color-middle)}.side-tabs__tab:hover:not(.side-tabs__tab--active):not(.side-tabs__tab--disabled){font-weight:800}.side-tabs__tab--active{background-color:var(--bg-color-dark);color:#fff;font-weight:800}.side-tabs__tab--active.dark{background-color:var(--bg-color)}.side-tabs__tab--disabled{color:#4b5563;cursor:not-allowed}.side-tabs__tab--active .side-tabs__indicator{background:var(--color-top);height:calc(100% + 4px);left:-4px;position:relative;width:3px}.alert.alert--info,.side-tabs__content.dark{background-color:var(--bg-color)}.side-tabs.small .side-tabs__tab--active .side-tabs__indicator{height:3px;left:-2px;position:relative;top:-3px;width:calc(100% + 4px)}.side-tabs.small .side-tabs__content{flex:1;margin-left:0!important;padding:20px}.side-tabs.small .side-tabs__list{cursor:grab;flex-direction:row!important;-webkit-mask-image:-webkit-gradient(linear,left top,right top,from(#000),color-stop(70%,#000),to(#0000));mask-image:-webkit-gradient(linear,left top,right top,from(#000),color-stop(70%,#000),to(#0000));overflow-x:auto;overflow-y:hidden;padding-right:80px;scroll-snap-type:x mandatory;scrollbar-width:none;white-space:nowrap}.side-tabs.small .side-tabs__list.is-dragging{cursor:grabbing}.side-tabs.small .side-tabs__list.is-dragging,.side-tabs.small .side-tabs__list.is-dragging *{-webkit-user-select:none;user-select:none}.side-tabs.small .side-tabs__tab{align-items:stretch;border-bottom-style:none;border-right-style:solid;border-right-width:2px;display:grid;flex:0 0 auto;gap:0}.side-tabs__label{flex:1;margin-left:22px;margin-right:22px;position:absolute;-webkit-user-select:none;user-select:none}.side-tabs.small .side-tabs__label{position:relative}.side-tabs.small .side-tabs__tab--active .side-tabs__label{margin-top:-3px}.side-tabs__content{background-color:var(--bg-color-dark);flex:1 1 0;font-size:16px;font-weight:500;line-height:25px;margin-left:2px;min-width:0;overflow-x:auto;padding:32px}.side-tabs__content strong{color:#f9fafb;font-weight:600}.alert.alert--info{border-radius:0}.alert.alert--info summary:before{border-color:#0000 #0000 #0000 var(--color-top)}.theme-doc-sidebar-container{clip-path:none!important;z-index:5}figure[data-rehype-pretty-code-figure]{margin:0 0 var(--ifm-leading);padding:0}pre[data-theme] code{background:#0000;border:none;border-radius:0!important;display:block!important;font-size:var(--ifm-code-font-size);line-height:var(--ifm-pre-line-height);padding:1rem}.theme-code-block pre{border:2px solid #666;border-radius:0}.theme-code-block,:not(pre)>code{border-radius:0!important}:not(pre)>code{background-color:var(--ifm-code-background);padding:var(--ifm-code-padding-vertical) var(--ifm-code-padding-horizontal)}.skipToContent_fXgn{background-color:var(--ifm-background-surface-color);color:var(--ifm-color-emphasis-900);left:100%;padding:calc(var(--ifm-global-spacing)/2) var(--ifm-global-spacing);position:fixed;top:1rem;z-index:calc(var(--ifm-z-index-fixed) + 1)}.skipToContent_fXgn:focus{box-shadow:var(--ifm-global-shadow-md);left:1rem}.closeButton_CVFx{line-height:0;padding:0}.content_knG7{font-size:85%;padding:5px 0;text-align:center}.content_knG7 a{color:inherit}.announcementBar_mb4j{align-items:center;background-color:var(--ifm-color-white);border-bottom:1px solid var(--ifm-color-emphasis-100);color:var(--ifm-color-black);display:flex;height:var(--docusaurus-announcement-bar-height)}.announcementBarPlaceholder_vyr4{flex:0 0 10px}.announcementBarClose_gvF7{align-self:stretch;flex:0 0 30px}.toggle_vylO{height:2rem;width:2rem}.toggleButton_gllP{align-items:center;border-radius:50%;display:flex;height:100%;justify-content:center;transition:background var(--ifm-transition-fast);width:100%}.toggleButton_gllP:hover{background:var(--ifm-color-emphasis-200)}[data-theme-choice=dark] .darkToggleIcon_wfgR,[data-theme-choice=light] .lightToggleIcon_pyhR,[data-theme-choice=system] .systemToggleIcon_QzmC,[data-theme=dark] .themedComponent--dark_xIcU,[data-theme=light] .themedComponent--light_NVdE,html:not([data-theme]) .themedComponent--light_NVdE{display:initial}.toggleButtonDisabled_aARS{cursor:not-allowed}.darkNavbarColorModeToggle_Q0Zn:hover{background:var(--ifm-color-gray-800)}.tag_zVej{border:1px solid var(--docusaurus-tag-list-border);transition:border var(--ifm-transition-fast)}.tag_zVej:hover{--docusaurus-tag-list-border:var(--ifm-link-color);-webkit-text-decoration:none;text-decoration:none}.tagRegular_sFm0{border-radius:var(--ifm-global-radius);font-size:90%;padding:.2rem .5rem .3rem}.tagWithCount_h2kH{align-items:center;border-left:0;display:flex;padding:0 .5rem 0 1rem;position:relative}.tagWithCount_h2kH:after,.tagWithCount_h2kH:before{border:1px solid var(--docusaurus-tag-list-border);content:"";position:absolute;top:50%;transition:inherit}.tagWithCount_h2kH:before{border-bottom:0;border-right:0;height:1.18rem;right:100%;transform:translate(50%,-50%) rotate(-45deg);width:1.18rem}.tagWithCount_h2kH:after{border-radius:50%;height:.5rem;left:0;transform:translateY(-50%);width:.5rem}.tagWithCount_h2kH span{background:var(--ifm-color-secondary);border-radius:var(--ifm-global-radius);color:var(--ifm-color-black);font-size:.7rem;line-height:1.2;margin-left:.3rem;padding:.1rem .4rem}.tag_QGVx{display:inline-block;margin:0 .4rem .5rem 0}.backToTopButton_sjWU{background-color:var(--ifm-color-emphasis-200);border-radius:50%;bottom:1.3rem;box-shadow:var(--ifm-global-shadow-lw);height:3rem;opacity:0;position:fixed;right:1.3rem;transform:scale(0);transition:all var(--ifm-transition-fast) var(--ifm-transition-timing-default);visibility:hidden;width:3rem;z-index:calc(var(--ifm-z-index-fixed) - 1)}.backToTopButton_sjWU:after{background-color:var(--ifm-color-emphasis-1000);content:" ";display:inline-block;height:100%;-webkit-mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;mask:var(--ifm-menu-link-sublist-icon) 50%/2rem 2rem no-repeat;width:100%}.backToTopButtonShow_xfvO{opacity:1;transform:scale(1);visibility:visible}[data-theme=dark]:root{--docusaurus-collapse-button-bg:#ffffff0d;--docusaurus-collapse-button-bg-hover:#ffffff1a}.collapseSidebarButton_JQG6{display:none;margin:0}.categoryLinkLabel_W154,.linkLabel_WmDU{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden}.iconExternalLink_nPIU{margin-left:.3rem}.linkLabel_WmDU{line-clamp:2;-webkit-line-clamp:2}.categoryLink_byQd{overflow:hidden}.menu__link--sublist-caret:after{margin-left:var(--ifm-menu-link-padding-vertical)}.categoryLinkLabel_W154{flex:1;line-clamp:2;-webkit-line-clamp:2}.docsWrapper_hBAB{display:flex;flex:1 0 auto}.searchbox,.searchbox__input{box-sizing:border-box;display:inline-block}.algolia-docsearch-suggestion{border-bottom-color:#3a3dd1}.algolia-docsearch-suggestion--category-header{background-color:#4b54de}.algolia-docsearch-suggestion--highlight{color:#3a33d1}.algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--highlight{background-color:#4d47d5}.aa-cursor .algolia-docsearch-suggestion--content{color:#272296}.aa-cursor .algolia-docsearch-suggestion{background:#ebebfb}.searchbox{height:32px!important;position:relative;visibility:visible!important;white-space:nowrap;width:200px}.searchbox .algolia-autocomplete{display:block;height:100%;width:100%}.searchbox__wrapper{height:100%;position:relative;width:100%;z-index:999}.searchbox__input{appearance:none;background:#fff!important;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;font-size:12px;height:100%;padding:0 26px 0 32px;transition:box-shadow .4s,background .4s;white-space:normal;width:100%}.searchbox__reset,.searchbox__submit{font-size:inherit;-webkit-user-select:none;position:absolute}.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none}.searchbox__input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox__input:active,.searchbox__input:focus{background:#fff;box-shadow:inset 0 0 0 1px #aaa;outline:0}.searchbox__input::placeholder{color:#aaa}.searchbox__submit{background-color:#458ee100;border:0;border-radius:16px 0 0 16px;height:100%;left:0;margin:0;padding:0;right:inherit;text-align:center;top:0;user-select:none;width:32px}.searchbox__submit:before{content:"";display:inline-block;height:100%;margin-right:-4px;vertical-align:middle}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion,.dropdownNavbarItemMobile_J0Sd,.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit svg{fill:#6d7e96;height:14px;width:14px}.searchbox__reset{background:none;border:0;cursor:pointer;display:block;fill:#00000080;margin:0;padding:0;right:8px;top:8px;user-select:none}.searchbox__reset.hide{display:none}.searchbox__reset svg{display:block;height:8px;margin:4px;width:8px}.searchbox__input:valid~.searchbox__reset{animation-duration:.15s;animation-name:d;display:block}@keyframes d{0%{opacity:0;transform:translate3d(-20%,0,0)}to{opacity:1;transform:none}}.algolia-autocomplete .ds-dropdown-menu:before{background:#373940;border-radius:2px;border-right:1px solid #373940;border-top:1px solid #373940;content:"";display:block;height:14px;position:absolute;top:-7px;transform:rotate(-45deg);width:14px;z-index:1000}.algolia-autocomplete .ds-dropdown-menu{box-shadow:0 1px 0 0 #0003,0 2px 3px 0 #0000001a}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{background:#fff;border-radius:4px;overflow:auto;padding:0;position:relative}.algolia-autocomplete .algolia-docsearch-suggestion{display:block;overflow:hidden;padding:0;position:relative;-webkit-text-decoration:none;text-decoration:none}.algolia-autocomplete .ds-cursor .algolia-docsearch-suggestion--wrapper{background:#f1f1f1;box-shadow:inset -2px 0 0 #61dafb}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{background:#ffe564;padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight{background:inherit;color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{background:inherit;box-shadow:inset 0 -2px 0 0 #458ee1cc;color:inherit;padding:0 0 1px}.algolia-autocomplete .algolia-docsearch-suggestion--content{cursor:pointer;display:block;float:right;padding:5.33333px 0 5.33333px 10.66667px;position:relative;width:70%}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{background:#ececec;content:"";display:block;height:100%;left:-1px;position:absolute;top:0;width:1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{background-color:#373940;color:#fff;display:none;font-size:14px;font-weight:700;letter-spacing:.08em;margin:0;padding:5px 8px;position:relative;text-transform:uppercase}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{background-color:#fff;float:left;padding:8px 0 0;width:100%}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{color:#777;display:none;float:left;font-size:.9em;padding:5.33333px 10.66667px;position:relative;text-align:right;width:30%;word-wrap:break-word}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{background:#ececec;content:"";display:block;height:100%;position:absolute;right:0;top:0;width:1px}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column .algolia-docsearch-suggestion--highlight{background-color:inherit;color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{color:#02060c;font-size:.9em;font-weight:700;margin-bottom:4px}.algolia-autocomplete .algolia-docsearch-suggestion--text{color:#63676d;display:block;font-size:.85em;line-height:1.2em;padding-right:2px}.algolia-autocomplete .algolia-docsearch-suggestion--version{color:#a6aab1;display:block;font-size:.65em;padding-right:2px;padding-top:2px}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{background-color:#373940;font-size:1.2em;margin-top:-8px;padding:8px 0;text-align:center;width:100%}.algolia-autocomplete .algolia-docsearch-suggestion--no-results .algolia-docsearch-suggestion--text{color:#fff;margin-top:4px}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before,.navbarSearchContainer_dCNk:empty{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{background-color:#ebebeb;border:none;border-radius:3px;color:#222;font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace;font-size:90%;padding:1px 5px}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header{color:#fff;display:block}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary .algolia-docsearch-suggestion--subcategory-column,.tocCollapsibleContent_vkbj a{display:block}.algolia-autocomplete .algolia-docsearch-footer{background-color:#fff;float:right;font-size:0;height:30px;line-height:0;width:100%;z-index:2000}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 130 18'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='url(%2523a)' d='M59.4.02h13.3a2.37 2.37 0 0 1 2.38 2.37V15.6a2.37 2.37 0 0 1-2.38 2.36H59.4a2.37 2.37 0 0 1-2.38-2.36V2.38A2.37 2.37 0 0 1 59.4.02'/%3E%3Cpath fill='%2523FFF' d='M66.26 4.56c-2.82 0-5.1 2.27-5.1 5.08 0 2.8 2.28 5.07 5.1 5.07 2.8 0 5.1-2.26 5.1-5.07 0-2.8-2.28-5.07-5.1-5.07zm0 8.65c-2 0-3.6-1.6-3.6-3.56 0-1.97 1.6-3.58 3.6-3.58 1.98 0 3.6 1.6 3.6 3.58a3.58 3.58 0 0 1-3.6 3.57zm0-6.4v2.66c0 .07.08.13.15.1l2.4-1.24c.04-.02.06-.1.03-.14a2.96 2.96 0 0 0-2.46-1.5.1.1 0 0 0-.1.1zm-3.33-1.96-.3-.3a.78.78 0 0 0-1.12 0l-.36.36a.77.77 0 0 0 0 1.1l.3.3c.05.05.13.04.17 0 .2-.25.4-.5.6-.7.23-.23.46-.43.7-.6.07-.04.07-.1.03-.16zm5-.8V3.4a.78.78 0 0 0-.78-.78h-1.83a.78.78 0 0 0-.78.78v.63c0 .07.06.12.14.1a5.7 5.7 0 0 1 1.58-.22c.52 0 1.04.07 1.54.2a.1.1 0 0 0 .13-.1z'/%3E%3Cpath fill='%2523182359' d='M102.16 13.76c0 1.46-.37 2.52-1.12 3.2-.75.67-1.9 1-3.44 1-.56 0-1.74-.1-2.67-.3l.34-1.7c.78.17 1.82.2 2.36.2.86 0 1.48-.16 1.84-.5.37-.36.55-.88.55-1.57v-.35a6 6 0 0 1-.84.3 4.2 4.2 0 0 1-1.2.17 4.5 4.5 0 0 1-1.6-.28 3.4 3.4 0 0 1-1.26-.82 3.7 3.7 0 0 1-.8-1.35c-.2-.54-.3-1.5-.3-2.2 0-.67.1-1.5.3-2.06a3.9 3.9 0 0 1 .9-1.43 4.1 4.1 0 0 1 1.45-.92 5.3 5.3 0 0 1 1.94-.37c.7 0 1.35.1 1.97.2a16 16 0 0 1 1.6.33v8.46zm-5.95-4.2c0 .9.2 1.88.6 2.3.4.4.9.62 1.53.62q.51 0 .96-.15a2.8 2.8 0 0 0 .73-.33V6.7a8.5 8.5 0 0 0-1.42-.17c-.76-.02-1.36.3-1.77.8-.4.5-.62 1.4-.62 2.23zm16.13 0c0 .72-.1 1.26-.32 1.85a4.4 4.4 0 0 1-.9 1.53c-.38.42-.85.75-1.4.98-.54.24-1.4.37-1.8.37-.43 0-1.27-.13-1.8-.36a4.1 4.1 0 0 1-1.4-.97 4.5 4.5 0 0 1-.92-1.52 5 5 0 0 1-.33-1.84c0-.72.1-1.4.32-2s.53-1.1.92-1.5c.4-.43.86-.75 1.4-.98a4.55 4.55 0 0 1 1.78-.34 4.7 4.7 0 0 1 1.8.34c.54.23 1 .55 1.4.97q.57.63.9 1.5c.23.6.35 1.3.35 2zm-2.2 0c0-.92-.2-1.7-.6-2.22-.38-.54-.94-.8-1.64-.8-.72 0-1.27.26-1.67.8s-.58 1.3-.58 2.22c0 .93.2 1.56.6 2.1.38.54.94.8 1.64.8s1.25-.26 1.65-.8c.4-.55.6-1.17.6-2.1m6.97 4.7c-3.5.02-3.5-2.8-3.5-3.27L113.57.92l2.15-.34v10c0 .25 0 1.87 1.37 1.88v1.8zm3.77 0h-2.15v-9.2l2.15-.33v9.54zM119.8 3.74c.7 0 1.3-.58 1.3-1.3 0-.7-.58-1.3-1.3-1.3-.73 0-1.3.6-1.3 1.3 0 .72.58 1.3 1.3 1.3m6.43 1c.7 0 1.3.1 1.78.27.5.18.88.42 1.17.73.28.3.5.74.6 1.18.13.46.2.95.2 1.5v5.47a25 25 0 0 1-1.5.25q-1.005.15-2.25.15a6.8 6.8 0 0 1-1.52-.16 3.2 3.2 0 0 1-1.18-.5 2.46 2.46 0 0 1-.76-.9c-.18-.37-.27-.9-.27-1.44 0-.52.1-.85.3-1.2.2-.37.48-.67.83-.9a3.6 3.6 0 0 1 1.23-.5 7 7 0 0 1 2.2-.1l.83.16V8.4c0-.25-.03-.48-.1-.7a1.5 1.5 0 0 0-.3-.58c-.15-.18-.34-.3-.58-.4a2.5 2.5 0 0 0-.92-.17c-.5 0-.94.06-1.35.13-.4.08-.75.16-1 .25l-.27-1.74c.27-.1.67-.18 1.2-.28a9.3 9.3 0 0 1 1.65-.14zm.18 7.74c.66 0 1.15-.04 1.5-.1V10.2a5.1 5.1 0 0 0-2-.1c-.23.03-.45.1-.64.2a1.17 1.17 0 0 0-.47.38c-.13.17-.18.26-.18.52 0 .5.17.8.5.98.32.2.74.3 1.3.3zM84.1 4.8c.72 0 1.3.08 1.8.26.48.17.87.42 1.15.73.3.3.5.72.6 1.17.14.45.2.94.2 1.47v5.48a25 25 0 0 1-1.5.26c-.67.1-1.42.14-2.25.14a6.8 6.8 0 0 1-1.52-.16 3.2 3.2 0 0 1-1.18-.5 2.46 2.46 0 0 1-.76-.9c-.18-.38-.27-.9-.27-1.44 0-.53.1-.86.3-1.22s.5-.65.84-.88a3.6 3.6 0 0 1 1.24-.5 7 7 0 0 1 2.2-.1q.39.045.84.15v-.35c0-.24-.03-.48-.1-.7a1.5 1.5 0 0 0-.3-.58c-.15-.17-.34-.3-.58-.4a2.5 2.5 0 0 0-.9-.15c-.5 0-.96.05-1.37.12-.4.07-.75.15-1 .24l-.26-1.75c.27-.08.67-.17 1.18-.26a9 9 0 0 1 1.66-.15zm.2 7.73c.65 0 1.14-.04 1.48-.1v-2.17a5.1 5.1 0 0 0-1.98-.1c-.24.03-.46.1-.65.18a1.17 1.17 0 0 0-.47.4c-.12.17-.17.26-.17.52 0 .5.18.8.5.98.32.2.75.3 1.3.3zm8.68 1.74c-3.5 0-3.5-2.82-3.5-3.28L89.45.92 91.6.6v10c0 .25 0 1.87 1.38 1.88v1.8z'/%3E%3Cpath fill='%25231D3657' d='M5.03 11.03c0 .7-.26 1.24-.76 1.64q-.75.6-2.1.6c-.88 0-1.6-.14-2.17-.42v-1.2c.36.16.74.3 1.14.38.4.1.78.15 1.13.15.5 0 .88-.1 1.12-.3a.94.94 0 0 0 .35-.77.98.98 0 0 0-.33-.74c-.22-.2-.68-.44-1.37-.72-.72-.3-1.22-.62-1.52-1C.23 8.27.1 7.82.1 7.3c0-.65.22-1.17.7-1.55.46-.37 1.08-.56 1.86-.56.76 0 1.5.16 2.25.48l-.4 1.05c-.7-.3-1.32-.44-1.87-.44-.4 0-.73.08-.94.26a.9.9 0 0 0-.33.72c0 .2.04.38.12.52.08.15.22.3.42.4.2.14.55.3 1.06.52.58.24 1 .47 1.27.67s.47.44.6.7c.12.26.18.57.18.92zM9 13.27c-.92 0-1.64-.27-2.16-.8-.52-.55-.78-1.3-.78-2.24 0-.97.24-1.73.72-2.3.5-.54 1.15-.82 2-.82.78 0 1.4.25 1.85.72.46.48.7 1.14.7 1.97v.67H7.35c0 .58.17 1.02.46 1.33.3.3.7.47 1.24.47.36 0 .68-.04.98-.1a5 5 0 0 0 .98-.33v1.02a3.9 3.9 0 0 1-.94.32 5.7 5.7 0 0 1-1.08.1zm-.22-5.2c-.4 0-.73.12-.97.38s-.37.62-.42 1.1h2.7c0-.48-.13-.85-.36-1.1-.23-.26-.54-.38-.94-.38zm7.7 5.1-.26-.84h-.05c-.28.36-.57.6-.86.74-.28.13-.65.2-1.1.2-.6 0-1.05-.16-1.38-.48-.32-.32-.5-.77-.5-1.34 0-.62.24-1.08.7-1.4.45-.3 1.14-.47 2.07-.5l1.02-.03V9.2c0-.37-.1-.65-.27-.84-.17-.2-.45-.28-.82-.28-.3 0-.6.04-.88.13a7 7 0 0 0-.8.33l-.4-.9a4.4 4.4 0 0 1 1.05-.4 5 5 0 0 1 1.08-.12c.76 0 1.33.18 1.7.5q.6.495.6 1.56v4h-.9zm-1.9-.87c.47 0 .83-.13 1.1-.38.3-.26.43-.62.43-1.08v-.52l-.76.03c-.6.03-1.02.13-1.3.3s-.4.45-.4.82c0 .26.08.47.24.6.16.16.4.23.7.23zm7.57-5.2c.25 0 .46.03.62.06l-.12 1.18a2.4 2.4 0 0 0-.56-.06c-.5 0-.92.16-1.24.5-.3.32-.47.75-.47 1.27v3.1h-1.27V7.23h1l.16 1.05h.05c.2-.36.45-.64.77-.85a1.83 1.83 0 0 1 1.02-.3zm4.12 6.17c-.9 0-1.58-.27-2.05-.8-.47-.52-.7-1.27-.7-2.25 0-1 .24-1.77.73-2.3.5-.54 1.2-.8 2.12-.8.63 0 1.2.1 1.7.34l-.4 1c-.52-.2-.96-.3-1.3-.3-1.04 0-1.55.68-1.55 2.05 0 .67.13 1.17.38 1.5.26.34.64.5 1.13.5a3.23 3.23 0 0 0 1.6-.4v1.1a2.5 2.5 0 0 1-.73.28 4.4 4.4 0 0 1-.93.08m8.28-.1h-1.27V9.5c0-.45-.1-.8-.28-1.02-.18-.23-.47-.34-.88-.34-.53 0-.9.16-1.16.48-.25.3-.38.85-.38 1.6v2.94h-1.26V4.8h1.26v2.12c0 .34-.02.7-.06 1.1h.08a1.76 1.76 0 0 1 .72-.67c.3-.16.66-.24 1.07-.24 1.43 0 2.15.74 2.15 2.2v3.86zM42.2 7.1c.74 0 1.32.28 1.73.82.4.53.62 1.3.62 2.26 0 .97-.2 1.73-.63 2.27-.42.54-1 .82-1.75.82s-1.33-.27-1.75-.8h-.08l-.23.7h-.94V4.8h1.26v2l-.02.64-.03.56h.05c.4-.6 1-.9 1.78-.9zm-.33 1.04c-.5 0-.88.15-1.1.45s-.34.8-.35 1.5v.08c0 .72.12 1.24.35 1.57.23.32.6.48 1.12.48.44 0 .78-.17 1-.53.24-.35.36-.87.36-1.53 0-1.35-.47-2.03-1.4-2.03zm3.24-.92h1.4l1.2 3.37c.18.47.3.92.36 1.34h.04l.18-.72 1.37-4H51l-2.53 6.73c-.46 1.23-1.23 1.85-2.3 1.85-.3 0-.56-.03-.83-.1v-1c.2.05.4.08.65.08.6 0 1.03-.36 1.28-1.06l.22-.56-2.4-5.94z'/%3E%3C/g%3E%3C/svg%3E");background-position:50%;background-repeat:no-repeat;background-size:100%;display:block;height:100%;margin-left:auto;margin-right:5px;overflow:hidden;text-indent:-9000px;width:110px}html[data-theme=dark] .algolia-docsearch-footer,html[data-theme=dark] .algolia-docsearch-suggestion--category-header,html[data-theme=dark] .algolia-docsearch-suggestion--wrapper{background:var(--ifm-background-color)!important;color:var(--ifm-font-color-base)!important}html[data-theme=dark] .algolia-docsearch-suggestion--title{color:var(--ifm-font-color-base)!important}html[data-theme=dark] .ds-cursor .algolia-docsearch-suggestion--wrapper{background:var(--ifm-background-surface-color)!important}mark{background-color:#add8e6}.theme-code-block:hover .copyButtonCopied_Vdqa{opacity:1!important}.copyButtonIcons_IEyt{height:1.125rem;position:relative;width:1.125rem}.copyButtonIcon_TrPX,.copyButtonSuccessIcon_cVMy{fill:currentColor;height:inherit;left:0;opacity:inherit;position:absolute;top:0;transition:all var(--ifm-transition-fast) ease;width:inherit}.copyButtonSuccessIcon_cVMy{color:#00d600;left:50%;opacity:0;top:50%;transform:translate(-50%,-50%) scale(.33)}.buttonGroup_M5ko button,.codeBlockContainer_Ckt0{background:var(--prism-background-color);color:var(--prism-color)}.copyButtonCopied_Vdqa .copyButtonIcon_TrPX{opacity:0;transform:scale(.33)}.copyButtonCopied_Vdqa .copyButtonSuccessIcon_cVMy{opacity:1;transform:translate(-50%,-50%) scale(1);transition-delay:75ms}.wordWrapButtonIcon_b1P5{height:1.2rem;width:1.2rem}.buttonGroup_M5ko{column-gap:.2rem;display:flex;position:absolute;right:calc(var(--ifm-pre-padding)/2);top:calc(var(--ifm-pre-padding)/2)}.buttonGroup_M5ko button{align-items:center;border:1px solid var(--ifm-color-emphasis-300);border-radius:var(--ifm-global-radius);display:flex;line-height:0;opacity:0;padding:.4rem;transition:opacity var(--ifm-transition-fast) ease-in-out}.buttonGroup_M5ko button:focus-visible,.buttonGroup_M5ko button:hover{opacity:1!important}.theme-code-block:hover .buttonGroup_M5ko button{opacity:.4}.codeBlockContainer_Ckt0{border-radius:var(--ifm-code-border-radius);box-shadow:var(--ifm-global-shadow-lw);margin-bottom:var(--ifm-leading)}.iconLanguage_nlXk{margin-right:5px;vertical-align:text-bottom}.navbarHideable_jvwV{transition:transform var(--ifm-transition-fast) ease}.navbarHidden_nLSi{transform:translate3d(0,calc(-100% - 2px),0)}.errorBoundaryError_a6uf{color:red;white-space:pre-wrap}.errorBoundaryFallback_VBag{color:red;padding:.55rem}.navbar__items--right>:last-child{padding-right:0}.anchorTargetStickyNavbar_Vzrq{scroll-margin-top:calc(var(--ifm-navbar-height) + .5rem)}.anchorTargetHideOnScrollNavbar_vjPI{scroll-margin-top:.5rem}.hash-link{opacity:0;padding-left:.5rem;transition:opacity var(--ifm-transition-fast);-webkit-user-select:none;user-select:none}.hash-link:before{content:"#"}.mainWrapper_z2l0{display:flex;flex:1 0 auto;flex-direction:column}.docusaurus-mt-lg{margin-top:3rem}#__docusaurus{position:relative;display:flex;flex-direction:column;min-height:100%}.iconEdit_Z9Sw{margin-right:.3em;vertical-align:sub}.details_lb9f{--docusaurus-details-summary-arrow-size:0.38rem;--docusaurus-details-transition:transform 200ms ease;--docusaurus-details-decoration-color:grey}.details_lb9f>summary{cursor:pointer;padding-left:1rem;position:relative}.details_lb9f>summary::-webkit-details-marker{display:none}.details_lb9f>summary:before{border-color:#0000 #0000 #0000 var(--docusaurus-details-decoration-color);border-style:solid;border-width:var(--docusaurus-details-summary-arrow-size);content:"";left:0;position:absolute;top:.45rem;transform:rotate(0);transform-origin:calc(var(--docusaurus-details-summary-arrow-size)/2) 50%;transition:var(--docusaurus-details-transition)}.collapsibleContent_i85q{border-top:1px solid var(--docusaurus-details-decoration-color);margin-top:1rem;padding-top:1rem}.lastUpdated_JAkA{font-size:smaller;font-style:italic;margin-top:.2rem}.tocCollapsibleButton_TO0P{align-items:center;display:flex;font-size:inherit;justify-content:space-between;padding:.4rem .8rem;width:100%}.tocCollapsibleButton_TO0P:after{background:var(--ifm-menu-link-sublist-icon) 50% 50%/2rem 2rem no-repeat;content:"";filter:var(--ifm-menu-link-sublist-icon-filter);height:1.25rem;transform:rotate(180deg);transition:transform var(--ifm-transition-fast);width:1.25rem}.tocCollapsibleButtonExpanded_MG3E:after,.tocCollapsibleExpanded_sAul{transform:none}.tocCollapsible_ETCw{background-color:var(--ifm-menu-color-background-active);border-radius:var(--ifm-global-radius);margin:1rem 0}.tocCollapsibleContent_vkbj>ul{border-left:none;border-top:1px solid var(--ifm-color-emphasis-300);font-size:15px;padding:.2rem 0}.tocCollapsibleContent_vkbj ul li{margin:.4rem .8rem}.details_b_Ee{--docusaurus-details-decoration-color:var(--ifm-alert-border-color);--docusaurus-details-transition:transform var(--ifm-transition-fast) ease;border:1px solid var(--ifm-alert-border-color);margin:0 0 var(--ifm-spacing-vertical)}:not(.containsTaskList_mC6p>li)>.containsTaskList_mC6p{padding-left:0}.img_ev3q{height:auto}.tableOfContents_bqdL{max-height:calc(100vh - var(--ifm-navbar-height) - 2rem);overflow-y:auto;position:sticky;top:calc(var(--ifm-navbar-height) + 1rem)}.admonition_xJq3{margin-bottom:1em}.admonitionHeading_Gvgb{font:var(--ifm-heading-font-weight) var(--ifm-h5-font-size)/var(--ifm-heading-line-height) var(--ifm-heading-font-family)}.admonitionHeading_Gvgb:not(:last-child){margin-bottom:.3rem}.admonitionHeading_Gvgb code{text-transform:none}.admonitionIcon_Rf37{display:inline-block;margin-right:.4em;vertical-align:middle}.admonitionIcon_Rf37 svg{display:inline-block;fill:var(--ifm-alert-foreground-color);height:1.6em;width:1.6em}.breadcrumbHomeIcon_YNFT{height:1.1rem;position:relative;top:1px;vertical-align:top;width:1.1rem}.breadcrumbsContainer_Z_bl{--ifm-breadcrumb-size-multiplier:0.8;margin-bottom:.8rem}.mdxPageWrapper_j9I6{justify-content:center}@media (min-width:576px);@media (min-width:601px){.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{left:inherit!important;right:0!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete .ds-dropdown-menu{background:#0000;border:none;border-radius:4px;height:auto;margin:6px 0 0;max-width:600px;min-width:500px;padding:0;position:relative;text-align:left;top:-6px;z-index:999}}@media (min-width:768px){.algolia-docsearch-suggestion{border-bottom-color:#7671df}.algolia-docsearch-suggestion--subcategory-column{border-right-color:#7671df;color:#4e4726}}@media (min-width:997px){.collapseSidebarButton_JQG6,.expandButton_TmdG{background-color:var(--docusaurus-collapse-button-bg)}:root{--docusaurus-announcement-bar-height:30px}.announcementBarClose_gvF7,.announcementBarPlaceholder_vyr4{flex-basis:50px}.collapseSidebarButton_JQG6{border:1px solid var(--ifm-toc-border-color);border-radius:0;bottom:0;display:block!important;height:40px;position:sticky}.collapseSidebarButtonIcon_Iseg{margin-top:4px;transform:rotate(180deg)}.expandButtonIcon_i1dp,[dir=rtl] .collapseSidebarButtonIcon_Iseg{transform:rotate(0)}.collapseSidebarButton_JQG6:focus,.collapseSidebarButton_JQG6:hover,.expandButton_TmdG:focus,.expandButton_TmdG:hover{background-color:var(--docusaurus-collapse-button-bg-hover)}.menuHtmlItem_M9Kj{padding:var(--ifm-menu-link-padding-vertical) var(--ifm-menu-link-padding-horizontal)}.menu_Y1UP{flex-grow:1;padding:.5rem}@supports (scrollbar-gutter:stable){.menu_Y1UP{padding:.5rem 0 .5rem .5rem;scrollbar-gutter:stable}}.menuWithAnnouncementBar_fPny{margin-bottom:var(--docusaurus-announcement-bar-height)}.sidebar_mhZE{display:flex;flex-direction:column;height:100%;padding-top:var(--ifm-navbar-height);width:var(--doc-sidebar-width)}.sidebarWithHideableNavbar__6UL{padding-top:0}.sidebarHidden__LRd{opacity:0;visibility:hidden}.sidebarLogo_F_0z{align-items:center;color:inherit!important;display:flex!important;margin:0 var(--ifm-navbar-padding-horizontal);max-height:var(--ifm-navbar-height);min-height:var(--ifm-navbar-height);-webkit-text-decoration:none!important;text-decoration:none!important}.sidebarLogo_F_0z img{height:2rem;margin-right:.5rem}.expandButton_TmdG{align-items:center;display:flex;height:100%;justify-content:center;position:absolute;right:0;top:0;transition:background-color var(--ifm-transition-fast) ease;width:100%}[dir=rtl] .expandButtonIcon_i1dp{transform:rotate(180deg)}.docSidebarContainer_YfHR{border-right:1px solid var(--ifm-toc-border-color);clip-path:inset(0);display:block;margin-top:calc(var(--ifm-navbar-height)*-1);transition:width var(--ifm-transition-fast) ease;width:var(--doc-sidebar-width);will-change:width}.docSidebarContainerHidden_DPk8{cursor:pointer;width:var(--doc-sidebar-hidden-width)}.sidebarViewport_aRkj{height:100%;max-height:100vh;position:sticky;top:0}.docMainContainer_TBSr{flex-grow:1;max-width:calc(100% - var(--doc-sidebar-width))}.docMainContainerEnhanced_lQrH{max-width:calc(100% - var(--doc-sidebar-hidden-width))}.docItemWrapperEnhanced_JWYK{max-width:calc(var(--ifm-container-width) + var(--doc-sidebar-width))!important}.navbarSearchContainer_dCNk{padding:0 var(--ifm-navbar-item-padding-horizontal)}.lastUpdated_JAkA{text-align:right}.tocMobile_ITEo{display:none}.docItemCol_z5aJ{max-width:75%!important}}@media (min-width:1440px){.container{max-width:var(--ifm-container-width-xl)}}@media (max-width:1200px){.premium-blocks{width:90%}}@media (max-width:1100px){.premium-blocks{grid-template-columns:repeat(3,1fr)}}@media (max-width:1050px){.premium-faq details{width:80%}}@media (max-width:1000px){.navbar-sidebar__item.menu{padding-top:50px}.navbar-sidebar .navbar-sidebar__brand{padding-top:16px}.premium-blocks{grid-template-columns:repeat(3,1fr)}.navbar{padding:2px 0}.navbar__logo{height:30px}.navbar .navbar__brand{margin-left:0}.navbar .navbar__inner{padding:16px}.navbar-line{top:65.5px}.navbar__search-input{width:100%!important}.algolia-autocomplete.algolia-autocomplete-left{position:static!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;position:relative!important;top:-6px!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:1rem}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:not(:has(.ds-suggestion)):before{display:none}}@media (max-width:996px){.col{--ifm-col-width:100%;flex-basis:var(--ifm-col-width);margin-left:0}.footer{--ifm-footer-padding-horizontal:0}.colorModeToggle_x44X,.footer__link-separator,.navbar__item,.tableOfContents_bqdL{display:none}.footer__col{margin-bottom:calc(var(--ifm-spacing-vertical)*3)}.footer__link-item{display:block;width:max-content}.hero{padding-left:0;padding-right:0}.navbar>.container,.navbar>.container-fluid{padding:0}.navbar__toggle{display:inherit}.navbar__search-input{width:9rem}.pills--block,.tabs--block{flex-direction:column}.navbarSearchContainer_dCNk{position:absolute;right:var(--ifm-navbar-padding-horizontal)}.docItemContainer_F8PC{padding:0 .3rem}}@media (max-width:900px){.display-flex-grid{display:grid}.gap-20-0{gap:0}.column-mobile,.flex-direction-default-column{flex-direction:column}.margin-top-125-0{margin-top:0}.flex-end-center,.flex-start-center,.justify-center-mobile{justify-content:center}.min-width-180-135{min-width:135px}.gap-30-16{gap:16px}.font-30-20{font-size:20px}.padding-right-20-0{padding-right:0}.margin-right-10-6{margin-right:6px}.margin-bottom-16-10{margin-bottom:10px}.width-140-120{width:120px}.margin-right-6-8{margin-right:8px}.padding-side-10-12,.padding-side-16-12{padding-left:12px;padding-right:12px}.padding-top-64-46{padding-top:46}.margin-left-10-0{margin-left:0}.auto-margin-mobile,.block h1,.block h2,.centered-mobile,.centered-mobile-p,.centered-smaller-mobile{margin-left:auto;margin-right:auto}.height-26-21{height:21px}.gap-24-20{gap:20px}.font-20-16{font-size:16px}.font-16-14,.font-20-14{font-size:14px}.hide-desktop{display:flex}.hide-mobile{display:none}.block{padding-top:30px}.block.tropy-before{padding-top:90px}.block .content{max-width:100%}.block .inner{flex-direction:column-reverse;gap:32px}.half p,.text-center-mobile{text-align:center}.block .inner .half{width:100%}.block .inner .half.right{overflow-x:scroll;overflow-y:hidden;scrollbar-width:none}.centered-mobile{width:min(1120px,74%)}.centered-mobile-p{max-width:88%;text-align:center}.centered-smaller-mobile{justify-content:center;width:min(1120px,68%)}.flex-wrap-mobile{flex-wrap:wrap}.grid-2-mobile{grid-template-columns:repeat(2,1fr)}.block h1,.block h2{font-size:26px;font-weight:800;max-width:92%}.framework-icon{height:30px;width:30px}.footer .footer-community-links{margin-top:32px}.hero-img{max-height:300px}}@media (max-width:800px){.premium-blocks,.premium-request iframe{width:100%}.price-calculator{width:96%}.price-calculator-inner{margin-left:2%;width:96%}.price-calculator .field label{width:30%}.price-calculator .field .input{width:65%}.premium-faq details{width:90%}}@media (max-width:700px){.premium-blocks{grid-template-columns:repeat(2,1fr)}.call-to-action .call-to-action-icon{display:contents}.navbar .call-to-action{margin-right:95px}.call-to-action .call-to-action-keyword{display:none}.call-to-action-popup{margin-right:5%;width:90%}}@media (max-width:600px){.samp-wrapper{width:90%}.algolia-autocomplete .ds-dropdown-menu{display:block;left:auto!important;max-height:calc(100% - 5rem);max-width:calc(100% - 2rem);position:fixed!important;right:1rem!important;top:50px!important;width:600px;z-index:100}.algolia-autocomplete .ds-dropdown-menu:before{right:6rem}}@media (max-width:576px){.markdown h1:first-child{--ifm-h1-font-size:2rem}.markdown>h2{--ifm-h2-font-size:1.5rem}.markdown>h3{--ifm-h3-font-size:1.25rem}}@media (max-width:500px){.navbar__search #search_input_react{width:6rem}}@media (max-width:480px){.padding-button{padding:6px 15px}.samp-wrapper{width:93%}}@media (max-width:400px){.tag-cloud-tag{margin:0 10px}}@media (max-width:340px){.navbar .text{padding-right:4px}}@media (hover:hover){.backToTopButton_sjWU:hover{background-color:var(--ifm-color-emphasis-300)}}@media (pointer:fine){.thin-scrollbar{scrollbar-width:thin}.thin-scrollbar::-webkit-scrollbar{height:var(--ifm-scrollbar-size);width:var(--ifm-scrollbar-size)}.thin-scrollbar::-webkit-scrollbar-track{background:var(--ifm-scrollbar-track-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb{background:var(--ifm-scrollbar-thumb-background-color);border-radius:10px}.thin-scrollbar::-webkit-scrollbar-thumb:hover{background:var(--ifm-scrollbar-thumb-hover-background-color)}}@media (prefers-reduced-motion:reduce){:root{--ifm-transition-fast:0ms;--ifm-transition-slow:0ms}}@media print{.announcementBar_mb4j,.footer,.menu,.navbar,.noPrint_WFHX,.pagination-nav,.table-of-contents,.tocMobile_ITEo{display:none}.tabs{page-break-inside:avoid}}
\ No newline at end of file
diff --git a/docs/assets/js/0027230a.2e976381.js b/docs/assets/js/0027230a.2e976381.js
deleted file mode 100644
index 3851ed6a09c..00000000000
--- a/docs/assets/js/0027230a.2e976381.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8382],{3520(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"rx-storage-lokijs","title":"Empower RxDB with the LokiJS RxStorage","description":"Discover the lightning-fast LokiJS RxStorage for RxDB. Explore in-memory speed, multi-tab support, and pros & cons of this unique storage solution.","source":"@site/docs/rx-storage-lokijs.md","sourceDirName":".","slug":"/rx-storage-lokijs.html","permalink":"/rx-storage-lokijs.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Empower RxDB with the LokiJS RxStorage","slug":"rx-storage-lokijs.html","description":"Discover the lightning-fast LokiJS RxStorage for RxDB. Explore in-memory speed, multi-tab support, and pros & cons of this unique storage solution."}}');var i=n(4848),o=n(8453);const a={title:"Empower RxDB with the LokiJS RxStorage",slug:"rx-storage-lokijs.html",description:"Discover the lightning-fast LokiJS RxStorage for RxDB. Explore in-memory speed, multi-tab support, and pros & cons of this unique storage solution."},t="RxStorage LokiJS",l={},d=[{value:"Pros",id:"pros",level:3},{value:"Cons",id:"cons",level:3},{value:"Usage",id:"usage",level:2},{value:"Adapters",id:"adapters",level:2},{value:"Multi-Tab support",id:"multi-tab-support",level:2},{value:"Autosave and autoload",id:"autosave-and-autoload",level:2},{value:"Known problems",id:"known-problems",level:2},{value:"Using the internal LokiJS database",id:"using-the-internal-lokijs-database",level:2},{value:"Disabling the non-premium console log",id:"disabling-the-non-premium-console-log",level:2}];function c(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"rxstorage-lokijs",children:"RxStorage LokiJS"})}),"\n",(0,i.jsxs)(s.p,{children:["The LokiJS RxStorage is based on ",(0,i.jsx)(s.a,{href:"https://github.com/techfort/LokiJS",children:"LokiJS"})," which is an ",(0,i.jsx)(s.strong,{children:"in-memory"})," database that processes all data in memory and only saves to disc when the app is closed or an interval is reached. This makes it very fast but you have the possibility to lose seemingly persisted writes when the JavaScript process ends before the persistence loop has been done."]}),"\n",(0,i.jsx)(s.admonition,{title:"LokiJS was removed in RxDB version 16",type:"warning",children:(0,i.jsxs)(s.p,{children:["The LokiJS project itself is no longer in development or maintained and therefore the lokijs RxStorage is ",(0,i.jsx)(s.strong,{children:"removed"}),". There are known bugs like having wrong query results of losing data. LokiJS bugs that occur outside of the RxDB layer will not be fixed and the LokiJS RxStorage was removed in RxDB version 16. Using LokiJS as storage is no longer possible. In production it is recommended to use another ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," instead. For browsers better use the ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," storage. For fast lazy persistence in memory data (similar to how lokijs works) you can use the ",(0,i.jsx)(s.a,{href:"/rx-storage-memory-mapped.html",children:"Memory Mapped"})," storage. If you really need the lokijs RxStorage, you can fork the open-source code from the previous RxDB version."]})}),"\n",(0,i.jsx)(s.h3,{id:"pros",children:"Pros"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Queries can run faster because all data is processed in memory."}),"\n",(0,i.jsx)(s.li,{children:"It has a much faster initial load time because it loads all data from IndexedDB in a single request. But this is only true for small datasets. If much data must is stored, the initial load time can be higher than on other RxStorage implementations."}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"cons",children:"Cons"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"It does not support attachments."}),"\n",(0,i.jsx)(s.li,{children:"Data can be lost when the JavaScript process is killed ungracefully like when the browser crashes or the power of the PC is terminated."}),"\n",(0,i.jsx)(s.li,{children:"All data must fit into the memory."}),"\n",(0,i.jsxs)(s.li,{children:["Slow initialisation time when used with ",(0,i.jsx)(s.code,{children:"multiInstance: true"})," because it has to await the leader election process."]}),"\n",(0,i.jsxs)(s.li,{children:["Slow initialisation time when really much data is stored inside of the database because it has to parse a big ",(0,i.jsx)(s.code,{children:"JSON"})," string."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageLoki"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-lokijs'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// in the browser, we want to persist data in IndexedDB, so we use the indexeddb adapter."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" LokiIncrementalIndexedDBAdapter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'lokijs/src/incremental-indexeddb-adapter'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLoki"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" adapter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" LokiIncrementalIndexedDBAdapter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* "})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Do not set lokiJS persistence options like autoload and autosave,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * RxDB will pick proper defaults based on the given adapter"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"adapters",children:"Adapters"}),"\n",(0,i.jsxs)(s.p,{children:["LokiJS is based on adapters that determine where to store persistent data. For LokiJS there are adapters for IndexedDB, AWS S3, the NodeJS filesystem or NativeScript.\nFind more about the possible adapters at the ",(0,i.jsx)(s.a,{href:"https://github.com/techfort/LokiJS/blob/master/tutorials/Persistence%20Adapters.md",children:"LokiJS docs"}),". For react native there is also the ",(0,i.jsx)(s.a,{href:"https://github.com/jonnyreeves/loki-async-reference-adapter",children:"loki-async-reference-adapter"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"multi-tab-support",children:"Multi-Tab support"}),"\n",(0,i.jsxs)(s.p,{children:["When you use plain LokiJS, you cannot build an app that can be used in multiple browser tabs. The reason is that LokiJS loads data in bulk and then only regularly persists the in-memory state to disc. When opened in multiple tabs, it would happen that the LokiJS instances overwrite each other and data is lost.\nWith the RxDB LokiJS-plugin, this problem is fixed with the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/broadcast-channel#using-the-leaderelection",children:"LeaderElection"})," module. Between all open tabs, a leading tab is elected and only in this tab a database is created. All other tabs do not run queries against their own database, but instead call the leading tab to send and retrieve data. When the leading tab is closed, a new leader is elected that reopens the database and processes queries. You can disable this by setting ",(0,i.jsx)(s.code,{children:"multiInstance: false"})," when creating the ",(0,i.jsx)(s.code,{children:"RxDatabase"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"autosave-and-autoload",children:"Autosave and autoload"}),"\n",(0,i.jsxs)(s.p,{children:["When using plain LokiJS, you could set the ",(0,i.jsx)(s.code,{children:"autosave"})," option to ",(0,i.jsx)(s.code,{children:"true"})," to make sure that LokiJS persists the database state after each write into the persistence adapter. Same goes to ",(0,i.jsx)(s.code,{children:"autoload"})," which loads the persisted state on database creation.\nBut RxDB knows better when to persist the database state and when to load it, so it has its own autosave logic. This will ensure that running the persistence handler does not affect the performance of more important tasks. Instead RxDB will always wait until the database is idle and then runs the persistence handler.\nA load of the persisted state is done on database or collection creation and it is ensured that multiple load calls do not run in parallel and interfere with each other or with ",(0,i.jsx)(s.code,{children:"saveDatabase()"})," calls."]}),"\n",(0,i.jsx)(s.h2,{id:"known-problems",children:"Known problems"}),"\n",(0,i.jsxs)(s.p,{children:["When you bundle the LokiJS Plugin with webpack, you might get the error ",(0,i.jsx)(s.code,{children:'Cannot find module "fs"'}),". This is because LokiJS uses a ",(0,i.jsx)(s.code,{children:"require('fs')"})," statement that cannot work in the browser.\nYou can fix that by telling webpack to not resolve the ",(0,i.jsx)(s.code,{children:"fs"})," module with the following block in your webpack config:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// in your webpack.config.js"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" resolve"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" fallback"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" fs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Or if you do not have a webpack.config.js like you do with angular,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// you might fix it by setting the browser field in the package.json"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "browser"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:": {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "fs"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsx)(s.h2,{id:"using-the-internal-lokijs-database",children:"Using the internal LokiJS database"}),"\n",(0,i.jsx)(s.p,{children:"For custom operations, you can access the internal LokiJS database.\nThis is dangerous because you might do changes that are not compatible with RxDB.\nOnly use this when there is no way to achieve your goals via the RxDB API."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storageInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".storageInstance;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storageInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"internals"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".localState;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"localState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" value"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" _deleted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" _attachments"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" _rev"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '1-62080c42d471e3d2625e49dcca3b8e3e'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" _meta"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lwt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getTime"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// manually trigger the save queue because we did a write to the internal loki db. "})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"databaseState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"saveQueue"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addWrite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"disabling-the-non-premium-console-log",children:"Disabling the non-premium console log"}),"\n",(0,i.jsxs)(s.p,{children:["We want to be transparent with our community, and you'll notice a console message when using the free Loki.js based RxStorage implementation. This message serves to inform you about the availability of faster storage solutions within our ",(0,i.jsx)(s.a,{href:"/premium/",children:"\ud83d\udc51 Premium Plugins"}),". We understand that this might be a minor inconvenience, and we sincerely apologize for that. However, maintaining and improving RxDB requires substantial resources, and our premium users help us ensure its sustainability. If you find value in RxDB and wish to remove this message, we encourage you to explore our premium storage options, which are optimized for professional use and production environments. Thank you for your understanding and support."]}),"\n",(0,i.jsxs)(s.p,{children:["If you already have premium access and want to use the LokiJS ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," without the log, you can call the ",(0,i.jsx)(s.code,{children:"setPremiumFlag()"})," function to disable the log."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { setPremiumFlag } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/shared'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"setPremiumFlag"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>t});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/01684a0a.ac267d98.js b/docs/assets/js/01684a0a.ac267d98.js
deleted file mode 100644
index 00526951e5a..00000000000
--- a/docs/assets/js/01684a0a.ac267d98.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6616],{7971(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>i,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"rx-storage-memory-synced","title":"Instant Performance with Memory Synced RxStorage","description":"Accelerate RxDB with in-memory storage replicated to disk. Enjoy instant queries, faster loads, and unstoppable performance for your web apps.","source":"@site/docs/rx-storage-memory-synced.md","sourceDirName":".","slug":"/rx-storage-memory-synced.html","permalink":"/rx-storage-memory-synced.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Instant Performance with Memory Synced RxStorage","slug":"rx-storage-memory-synced.html","description":"Accelerate RxDB with in-memory storage replicated to disk. Enjoy instant queries, faster loads, and unstoppable performance for your web apps."}}');var a=s(4848),o=s(8453);const i={title:"Instant Performance with Memory Synced RxStorage",slug:"rx-storage-memory-synced.html",description:"Accelerate RxDB with in-memory storage replicated to disk. Enjoy instant queries, faster loads, and unstoppable performance for your web apps."},t="Memory Synced RxStorage",l={},c=[{value:"Pros",id:"pros",level:2},{value:"Cons",id:"cons",level:2},{value:"Usage",id:"usage",level:2},{value:"Options",id:"options",level:2},{value:"Replication and Migration with the memory-synced storage",id:"replication-and-migration-with-the-memory-synced-storage",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"memory-synced-rxstorage",children:"Memory Synced RxStorage"})}),"\n",(0,a.jsxs)(n.p,{children:["The memory synced ",(0,a.jsx)(n.a,{href:"/rx-storage.html",children:"RxStorage"})," is a wrapper around any other RxStorage. The wrapper creates an in-memory storage that is used for query and write operations. This memory instance is replicated with the underlying storage for persistence.\nThe main reason to use this is to improve initial page load and query/write times. This is mostly useful in browser based applications."]}),"\n",(0,a.jsx)(n.h2,{id:"pros",children:"Pros"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Improves read/write performance because these operations run against the in-memory storage."}),"\n",(0,a.jsx)(n.li,{children:"Decreases initial page load because it load all data in a single bulk request. It even detects if the database is used for the first time and then it does not have to await the creation of the persistent storage."}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"cons",children:"Cons"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"It does not support attachments."}),"\n",(0,a.jsxs)(n.li,{children:["When the JavaScript process is killed ungracefully like when the browser crashes or the power of the PC is terminated, it might happen that some memory writes are not persisted to the parent storage. This can be prevented with the ",(0,a.jsx)(n.code,{children:"awaitWritePersistence"})," flag."]}),"\n",(0,a.jsx)(n.li,{children:"This can only be used if all data fits into the memory of the JavaScript process. This is normally not a problem because a browser has much memory these days and plain json document data is not that big."}),"\n",(0,a.jsxs)(n.li,{children:["Because it has to await an initial replication from the parent storage into the memory, initial page load time can increase when much data is already stored. This is likely not a problem when you store less than ",(0,a.jsx)(n.code,{children:"10k"})," documents."]}),"\n",(0,a.jsx)(n.li,{children:"The memory-synced storage itself does not support replication and migration. Instead you have to replicate the underlying parent storage."}),"\n",(0,a.jsxs)(n.li,{children:["The ",(0,a.jsx)(n.code,{children:"memory-synced"})," plugin is part of ",(0,a.jsx)(n.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"}),". It is not part of the default RxDB module."]}),"\n"]}),"\n",(0,a.jsx)(n.admonition,{title:"The memory-synced RxStorage was removed in RxDB version 16",type:"note",children:(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"memory-synced"})," was removed in RxDB version 16. Instead consider using the newer and better ",(0,a.jsx)(n.a,{href:"/rx-storage-memory-mapped.html",children:"memory-mapped RxStorage"})," which has better trade-offs and is easier to configure."]})}),"\n",(0,a.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getMemorySyncedRxStorage"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-memory-synced'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here we use the IndexedDB RxStorage as persistence storage."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Any other RxStorage can also be used."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" parentStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap the persistent storage with the memory synced one."})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getMemorySyncedRxStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" parentStorage"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// create the RxDatabase like you would do with any other RxStorage"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myDatabase,"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/** ... **/"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "})]})})}),"\n",(0,a.jsx)(n.h2,{id:"options",children:"Options"}),"\n",(0,a.jsx)(n.p,{children:"Some options can be provided to fine tune the performance and behavior."}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" requestIdlePromise"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getMemorySyncedRxStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" parentStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Defines how many document"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * get replicated in a single batch."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=50]"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * By default, the parent storage will be created without indexes for a faster page load."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Indexes are not needed because the queries will anyway run on the memory storage."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can disable this behavior by setting keepIndexesOnParent to true."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If you use the same parent storage for multiple RxDatabase instances where one is not"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * a asynced-memory storage, you will get the error: 'schema not equal to existing storage'"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * if you do not set keepIndexesOnParent to true."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" keepIndexesOnParent"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If set to true, all write operations will resolve AFTER the writes"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * have been persisted from the memory to the parentStorage."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This ensures writes are not lost even if the JavaScript process exits"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * between memory writes and the persistence interval."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * default=false"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" awaitWritePersistence"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * After a write, await until the return value of this method resolves"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * before replicating with the master storage."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * By returning requestIdlePromise() we can ensure that the CPU is idle"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * and no other, more important operation is running. By doing so we can be sure"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * that the replication does not slow down any rendering of the browser process."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" waitBeforePersist"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" requestIdlePromise"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(n.h2,{id:"replication-and-migration-with-the-memory-synced-storage",children:"Replication and Migration with the memory-synced storage"}),"\n",(0,a.jsxs)(n.p,{children:["The memory-synced storage itself does not support replication and migration. Instead you have to replicate the underlying parent storage.\nFor example when you use it on top of an ",(0,a.jsx)(n.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB storage"}),", you have to run replication on that storage instead by creating a different ",(0,a.jsx)(n.a,{href:"/rx-database.html",children:"RxDatabase"}),"."]}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" parentStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" memorySyncedStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getMemorySyncedRxStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" parentStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" keepIndexesOnParent"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" databaseName"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydata'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Create a parent database with the same name+collections"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * and use it for replication and migration."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The parent database must be created BEFORE the memory-synced database"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * to ensure migration has already been run."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" parentDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" databaseName"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" parentStorage"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" parentDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"replicateRxCollection"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" parentDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".myCollection"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Create an equal memory-synced database with the same name+collections"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * and use it for writes and queries."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" memoryDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" databaseName"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" memorySyncedStorage"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" memoryDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>i,x:()=>t});var r=s(6540);const a={},o=r.createContext(a);function i(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/01a106d5.2ac15d82.js b/docs/assets/js/01a106d5.2ac15d82.js
deleted file mode 100644
index 8da482e7cd2..00000000000
--- a/docs/assets/js/01a106d5.2ac15d82.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5423],{3247(e,s,n){n.d(s,{g:()=>i});var r=n(4848);function i(e){const s=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&s.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&s.push(n),(0,r.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:s+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,r.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},7450(e,s,n){n.r(s),n.d(s,{assets:()=>c,contentTitle:()=>t,default:()=>p,frontMatter:()=>l,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"react","title":"React","description":"RxDB provides first-class support for both React and React Native via a dedicated React integration. This integration makes it possible to use RxDB inside functional components using React Context and hooks, without manually subscribing to observables or managing cleanup logic.","source":"@site/docs/react.md","sourceDirName":".","slug":"/react.html","permalink":"/react.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"React","slug":"react.html"},"sidebar":"tutorialSidebar","previous":{"title":"Third Party Plugins","permalink":"/third-party-plugins.html"},"next":{"title":"RxStorage Performance","permalink":"/rx-storage-performance.html"}}');var i=n(4848),o=n(8453),a=(n(2636),n(3247));const l={title:"React",slug:"react.html"},t="React",c={},d=[{value:"General concept",id:"general-concept",level:2},{value:"Usage",id:"usage",level:2},{value:"Installation",id:"installation",level:3},{value:"Database creation",id:"database-creation",level:3},{value:"Providing the database",id:"providing-the-database",level:3},{value:"Accessing collections",id:"accessing-collections",level:3},{value:"Queries",id:"queries",level:3},{value:"Live queries",id:"live-queries",level:3},{value:"React Native compatibility",id:"react-native-compatibility",level:2},{value:"Signals",id:"signals",level:2},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"react",children:"React"})}),"\n",(0,i.jsx)(s.p,{children:"RxDB provides first-class support for both React and React Native via a dedicated React integration. This integration makes it possible to use RxDB inside functional components using React Context and hooks, without manually subscribing to observables or managing cleanup logic."}),"\n",(0,i.jsxs)(s.p,{children:["The same APIs work in ",(0,i.jsx)(s.strong,{children:"React"})," for the web and in ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"}),". The only difference between platforms is the storage and environment setup. The React integration itself behaves identically in both."]}),"\n",(0,i.jsx)(s.h2,{id:"general-concept",children:"General concept"}),"\n",(0,i.jsx)(s.p,{children:"RxDB is internally reactive and emits changes via RxJS observables. React and React Native, however, are render-driven frameworks that rely on component state and hooks. The RxDB React integration bridges these two models by exposing a small set of hooks that translate RxDB state into React renders."}),"\n",(0,i.jsx)(s.p,{children:"Instead of hiding reactivity behind configuration flags, the integration uses explicit hooks. This makes component behavior predictable, avoids accidental subscriptions, and keeps performance characteristics easy to reason about."}),"\n",(0,i.jsx)(s.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(s.h3,{id:"installation",children:"Installation"}),(0,i.jsx)(s.p,{children:"Install RxDB and React as usual:"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" react"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" react-dom"})]})})})}),(0,i.jsx)(s.h3,{id:"database-creation",children:"Database creation"}),(0,i.jsx)(s.p,{children:"Database creation is not part of the React integration itself. RxDB is created in the same way as in non-React applications, including storage selection, plugins, replication, hooks, and schema definitions."}),(0,i.jsx)(s.p,{children:"This separation is intentional. React components should never be responsible for creating or configuring the database. They should only consume it."}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" addRxPlugin"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageLocalstorage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesreactdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" myRxSchema"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Do other stuff here"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * like setting up middleware"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * or starting replication."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),(0,i.jsx)(s.h3,{id:"providing-the-database",children:"Providing the database"}),(0,i.jsxs)(s.p,{children:["To use RxDB in a React or React Native application, the database instance must be provided via a context. This is done using ",(0,i.jsx)(s.code,{children:"RxDatabaseProvider"}),"."]}),(0,i.jsx)(s.p,{children:"The database itself is created outside of React, usually in a separate module. The provider is only responsible for making the database available to components once it has been initialized."}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"tsx","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"tsx","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" React"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { useEffect"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" useState } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'react'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDatabaseProvider } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/react'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" './Database'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" App"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" setDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useEffect"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" setDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(db);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" []);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (database "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" null"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Loading <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"a"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" href"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"https://rxdb.info/react.html"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">RxDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"a"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"> database..."})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxDatabaseProvider"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{database}>"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* your application */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxDatabaseProvider"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" default"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" App;"})]})]})})}),(0,i.jsx)(s.h3,{id:"accessing-collections",children:"Accessing collections"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { useRxCollection } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/react'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'heroes'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),(0,i.jsxs)(s.p,{children:["The hook returns the collection once it becomes available. During the initial render, the value may be ",(0,i.jsx)(s.code,{children:"undefined"}),", so components must handle this case."]}),(0,i.jsx)(s.p,{children:"This hook does not subscribe to any data. It only provides access to the collection instance."}),(0,i.jsx)(s.h3,{id:"queries",children:"Queries"}),(0,i.jsxs)(s.p,{children:["To render query results in your component, use the ",(0,i.jsx)(s.code,{children:"useRxQuery"})," hook."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"tsx","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"tsx","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { useRxQuery } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/react'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroes'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCount"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"results"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" loading"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useRxQuery"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(query);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (loading) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">Loading..."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">Total heroes: {"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"results"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),(0,i.jsx)(s.p,{children:"The query is executed when the component renders. If the component re-renders, the query may be re-executed, but changes in the underlying data will not automatically trigger updates."}),(0,i.jsx)(s.p,{children:"This hook is well suited for static views, server-side rendering, and cases where live updates are not required."}),(0,i.jsx)(s.h3,{id:"live-queries",children:"Live queries"}),(0,i.jsxs)(s.p,{children:["Most React applications require views that automatically update when the database changes. For this purpose, RxDB provides the ",(0,i.jsx)(s.code,{children:"useLiveRxQuery"})," hook."]}),(0,i.jsx)(s.p,{children:"The hook accepts a query description object and returns the current results together with a loading state."}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"tsx","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"tsx","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { useLiveRxQuery } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/react'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroes'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" HeroList"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"results"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" loading"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useLiveRxQuery"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(query);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (loading) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">Loading..."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"results"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(hero "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"hero"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".name}>{"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"hero"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".name}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ))}"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),(0,i.jsx)(s.p,{children:"The component automatically re-renders whenever the query result changes. Subscriptions are created when the component mounts and are cleaned up automatically when the component unmounts."}),(0,i.jsx)(s.p,{children:"The returned documents are fully reactive RxDB documents and can be modified or removed directly."})]}),"\n",(0,i.jsx)(s.h2,{id:"react-native-compatibility",children:"React Native compatibility"}),"\n",(0,i.jsx)(s.p,{children:"All hooks and providers described on this page work the same way in React Native. The React integration does not rely on any browser-specific APIs."}),"\n",(0,i.jsx)(s.p,{children:"The only platform-specific part of a React Native setup is database creation, where a different storage plugin is typically used. Once the database is created, the React integration behaves identically."}),"\n",(0,i.jsx)(s.h2,{id:"signals",children:"Signals"}),"\n",(0,i.jsxs)(s.p,{children:["In addition to the React hooks shown on this page, RxDB also supports alternative reactivity models such as ",(0,i.jsx)(s.a,{href:"/reactivity.html#react",children:"signals"}),". RxDB's core reactivity system can be configured to expose reactive values using different primitives instead of RxJS observables, which makes it possible to integrate RxDB with signal-based approaches in React or other frameworks. This is an advanced capability and is independent of the React integration described here. For more details about how RxDB's reactivity system works and how custom reactivity can be configured, see the ",(0,i.jsx)(s.a,{href:"/reactivity.html",children:"Reactivity documentation"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:["RxDB includes a full ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/react",children:"React example application"})," that demonstrates the patterns described on this page, including database creation outside of React, usage of ",(0,i.jsx)(s.code,{children:"RxDatabaseProvider"}),", and data access via ",(0,i.jsx)(s.code,{children:"useRxQuery"}),", ",(0,i.jsx)(s.code,{children:"useLiveRxQuery"}),", and `useRxCollection."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:["A corresponding ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/react-native",children:"React Native example"})," is also available and shows the same integration concepts applied in a mobile environment, with only the platform-specific storage and setup differing."]}),"\n"]}),"\n"]})]})}function p(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/03e37916.82c0454b.js b/docs/assets/js/03e37916.82c0454b.js
deleted file mode 100644
index fd427026789..00000000000
--- a/docs/assets/js/03e37916.82c0454b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6465],{5522(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>r,default:()=>d,frontMatter:()=>o,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"transactions-conflicts-revisions","title":"Transactions, Conflicts and Revisions","description":"Learn RxDB\'s approach to local and replication conflicts. Discover how incremental writes and custom handlers keep your app stable and efficient.","source":"@site/docs/transactions-conflicts-revisions.md","sourceDirName":".","slug":"/transactions-conflicts-revisions.html","permalink":"/transactions-conflicts-revisions.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Transactions, Conflicts and Revisions","slug":"transactions-conflicts-revisions.html","description":"Learn RxDB\'s approach to local and replication conflicts. Discover how incremental writes and custom handlers keep your app stable and efficient."},"sidebar":"tutorialSidebar","previous":{"title":"RxServer Scaling","permalink":"/rx-server-scaling.html"},"next":{"title":"Query Cache","permalink":"/query-cache.html"}}');var i=t(4848),a=t(8453);const o={title:"Transactions, Conflicts and Revisions",slug:"transactions-conflicts-revisions.html",description:"Learn RxDB's approach to local and replication conflicts. Discover how incremental writes and custom handlers keep your app stable and efficient."},r="Transactions, Conflicts and Revisions",l={},c=[{value:"Why RxDB does not have transactions",id:"why-rxdb-does-not-have-transactions",level:2},{value:"Revisions",id:"revisions",level:2},{value:"Conflicts",id:"conflicts",level:2},{value:"Local conflicts",id:"local-conflicts",level:3},{value:"Replication conflicts",id:"replication-conflicts",level:2},{value:"Custom conflict handler",id:"custom-conflict-handler",level:2}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"transactions-conflicts-and-revisions",children:"Transactions, Conflicts and Revisions"})}),"\n",(0,i.jsx)(n.p,{children:"In contrast to most SQL databases, RxDB does not have the concept of relational, ACID transactions. Instead, RxDB has to apply different techniques that better suit the offline-first, client side world where it is not possible to create a transaction between multiple maybe-offline client devices."}),"\n",(0,i.jsx)(n.h2,{id:"why-rxdb-does-not-have-transactions",children:"Why RxDB does not have transactions"}),"\n",(0,i.jsxs)(n.p,{children:["When talking about transactions, we mean ",(0,i.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/ACID",children:"ACID transactions"})," that guarantee the properties of atomicity, consistency, isolation and durability.\nWith an ACID transaction you can mutate data dependent on the current state of the database. It is ensured that no other database operations happen in between your transaction and after the transaction has finished, it is guaranteed that the new data is actually written to the disc."]}),"\n",(0,i.jsxs)(n.p,{children:["To implement ACID transactions on a ",(0,i.jsx)(n.strong,{children:"single server"}),", the database has to keep track on who is running transactions and then schedule these transactions so that they can run in isolation."]}),"\n",(0,i.jsxs)(n.p,{children:["As soon as you have to split your database on ",(0,i.jsx)(n.strong,{children:"multiple servers"}),", transaction handling becomes way more difficult. The servers have to communicate with each other to find a consensus about which transaction can run and which has to wait. Network connections might break, or one server might complete its part of the transaction and then be required to roll back its changes because of an error on another server."]}),"\n",(0,i.jsxs)(n.p,{children:["But with RxDB you have ",(0,i.jsx)(n.strong,{children:"multiple clients"})," that can go randomly online or offline. The users can have different devices and the clock of these devices can go off by any time. To support ACID transactions here, RxDB would have to make the whole world stand still for all clients, while one client is doing a write operation. And even that can only work when all clients are online. Implementing that might be possible, but at the cost of an unpredictable amount of performance loss and not being able to support ",(0,i.jsx)(n.a,{href:"/offline-first.html",children:"offline-first"}),"."]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"A single write operation to a document is the only atomic thing you can do in RxDB."}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"The benefits of not having to support transactions:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Clients can read and write data without blocking each other."}),"\n",(0,i.jsxs)(n.li,{children:["Clients can write data while being ",(0,i.jsx)(n.strong,{children:"offline"})," and then replicate with a server when they are ",(0,i.jsx)(n.strong,{children:"online"})," again, called ",(0,i.jsx)(n.a,{href:"/offline-first.html",children:"offline-first"}),"."]}),"\n",(0,i.jsx)(n.li,{children:"Creating a compatible backend for the replication is easy so that RxDB can replicate with any existing infrastructure."}),"\n",(0,i.jsxs)(n.li,{children:["Optimizations like ",(0,i.jsx)(n.a,{href:"/rx-storage-sharding.html",children:"Sharding"})," can be used."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"revisions",children:"Revisions"}),"\n",(0,i.jsx)(n.p,{children:"Working without transactions leads to having undefined state when doing multiple database operations at the same time. Most client side databases rely on a last-write-wins strategy on write operations. This might be a viable solution for some cases, but often this leads to strange problems that are hard to debug."}),"\n",(0,i.jsxs)(n.p,{children:["Instead, to ensure that the behavior of RxDB is ",(0,i.jsx)(n.strong,{children:"always predictable"}),", RxDB relies on ",(0,i.jsx)(n.strong,{children:"revisions"})," for version control. Revisions work similar to ",(0,i.jsx)(n.a,{href:"https://martinfowler.com/articles/patterns-of-distributed-systems/lamport-clock.html",children:"Lamport Clocks"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["Each document is stored together with its revision string, that looks like ",(0,i.jsx)(n.code,{children:"1-9dcca3b8e1a"})," and consists of:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["The revision height, a number that starts with ",(0,i.jsx)(n.code,{children:"1"})," and is increased with each write to that document."]}),"\n",(0,i.jsx)(n.li,{children:"The database instance token."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["An operation to the RxDB data layer does not only contain the new document data, but also the previous document data with its revision string. If the previous revision matches the revision that is currently stored in the database, the write operation can succeed. If the previous revision is ",(0,i.jsx)(n.strong,{children:"different"})," than the revision that is currently stored in the database, the operation will throw a ",(0,i.jsx)(n.code,{children:"409 CONFLICT"})," error."]}),"\n",(0,i.jsx)(n.h2,{id:"conflicts",children:"Conflicts"}),"\n",(0,i.jsxs)(n.p,{children:["There are two types of conflicts in RxDB, the ",(0,i.jsx)(n.strong,{children:"local conflict"})," and the ",(0,i.jsx)(n.strong,{children:"replication conflict"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"local-conflicts",children:"Local conflicts"}),"\n",(0,i.jsx)(n.p,{children:"A local conflict can happen when a write operation assumes a different previous document state, than what is currently stored in the database. This can happen when multiple parts of your application do simultaneous writes to the same document. This can happen on a single browser tab, or when multiple tabs write at once or when a write appears while the document gets replicated from a remote server replication."}),"\n",(0,i.jsxs)(n.p,{children:["When a local conflict appears, RxDB will throw a ",(0,i.jsx)(n.code,{children:"409 CONFLICT"})," error. The calling code must then handle the error properly, depending on the application logic."]}),"\n",(0,i.jsxs)(n.p,{children:["Instead of handling local conflicts, in most cases it is easier to ensure that they cannot happen, by using ",(0,i.jsx)(n.code,{children:"incremental"})," database operations like ",(0,i.jsx)(n.a,{href:"/rx-document.html",children:"incrementalModify()"}),", ",(0,i.jsx)(n.a,{href:"/rx-document.html",children:"incrementalPatch()"})," or ",(0,i.jsx)(n.a,{href:"/rx-collection.html",children:"incrementalUpsert()"}),". These write operations have a built-in way to handle conflicts by re-applying the mutation functions to the conflicting document state."]}),"\n",(0,i.jsx)(n.h2,{id:"replication-conflicts",children:"Replication conflicts"}),"\n",(0,i.jsx)(n.p,{children:"A replication conflict appears when multiple clients write to the same documents at once and these documents are then replicated to the backend server."}),"\n",(0,i.jsxs)(n.p,{children:["When you replicate with the ",(0,i.jsx)(n.a,{href:"/replication-graphql.html",children:"Graphql replication"})," and the ",(0,i.jsx)(n.a,{href:"/replication.html",children:"replication primitives"}),", RxDB assumes that conflicts are ",(0,i.jsx)(n.strong,{children:"detected"})," and ",(0,i.jsx)(n.strong,{children:"resolved"})," at the client side."]}),"\n",(0,i.jsxs)(n.p,{children:["When a document is send to the backend and the backend detected a conflict (by comparing revisions or other properties), the backend will respond with the actual document state so that the client can compare this with the local document state and create a new, resolved document state that is then pushed to the server again. You can read more about the replication conflicts ",(0,i.jsx)(n.a,{href:"/replication.html#conflict-handling",children:"here"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"custom-conflict-handler",children:"Custom conflict handler"}),"\n",(0,i.jsx)(n.p,{children:"A conflict handler is an object with two JavaScript functions:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Detect if two document states are equal"}),"\n",(0,i.jsx)(n.li,{children:"Solve existing conflicts"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"Because the conflict handler also is used for conflict detection, it will run many times on pull-, push- and write operations of RxDB. Most of the time it will detect that there is no conflict and then return."}),"\n",(0,i.jsxs)(n.p,{children:["Lets have a look at the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/src/replication-protocol/default-conflict-handler.ts",children:"default conflict handler"})," of RxDB to learn how to create a custom one:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { deepEqual } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/utils'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" defaultConflictHandler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" RxConflictHandler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"any"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"> "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" isEqual"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" b) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * isEqual() is used to detect conflicts or to detect if a"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * document has to be pushed to the remote."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If the documents are deep equal,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * we have no conflict."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Because deepEqual is CPU expensive, on your custom conflict handler you might only"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * check some properties, like the updatedAt time or revisions"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * for better performance."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" deepEqual"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" b);"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" resolve"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(i) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The default conflict handler will always"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * drop the fork state and use the master state instead."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * In your custom conflict handler you likely want to merge properties"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * of the realMasterState and the newDocumentState instead."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" i"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".realMasterState;"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["To overwrite the default conflict handler, you have to specify a custom ",(0,i.jsx)(n.code,{children:"conflictHandler"})," property when creating a collection with ",(0,i.jsx)(n.code,{children:"addCollections()"}),"."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollections"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // key = collectionName"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" conflictHandler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCustomConflictHandler"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]})}function d(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,n,t){t.d(n,{R:()=>o,x:()=>r});var s=t(6540);const i={},a=s.createContext(i);function o(e){const n=s.useContext(a);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),s.createElement(a.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/045bd6f5.d7543fa1.js b/docs/assets/js/045bd6f5.d7543fa1.js
deleted file mode 100644
index 5f5d4420bd3..00000000000
--- a/docs/assets/js/045bd6f5.d7543fa1.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4475],{3247(e,r,n){n.d(r,{g:()=>i});var s=n(4848);function i(e){const r=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&r.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&r.push(n),(0,s.jsx)("div",{style:o.stepsContainer,children:r.map((e,r)=>(0,s.jsxs)("div",{style:o.stepWrapper,children:[(0,s.jsxs)("div",{style:o.stepIndicator,children:[(0,s.jsx)("div",{style:o.stepNumber,children:r+1}),(0,s.jsx)("div",{style:o.verticalLine})]}),(0,s.jsxs)("div",{style:o.stepContent,children:[(0,s.jsx)("div",{children:e.headline}),e.paragraphs.map((e,r)=>(0,s.jsx)("div",{style:o.item,children:e},r))]})]},r))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},8453(e,r,n){n.d(r,{R:()=>a,x:()=>t});var s=n(6540);const i={},o=s.createContext(i);function a(e){const r=s.useContext(o);return s.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function t(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),s.createElement(o.Provider,{value:r},e.children)}},9978(e,r,n){n.r(r),n.d(r,{assets:()=>d,contentTitle:()=>l,default:()=>p,frontMatter:()=>t,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"encryption","title":"Encryption","description":"Explore RxDB\'s \ud83d\udd12 encryption plugin for enhanced data security in web and native apps, featuring password-based encryption and secure storage.","source":"@site/docs/encryption.md","sourceDirName":".","slug":"/encryption.html","permalink":"/encryption.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Encryption","slug":"encryption.html","description":"Explore RxDB\'s \ud83d\udd12 encryption plugin for enhanced data security in web and native apps, featuring password-based encryption and secure storage."},"sidebar":"tutorialSidebar","previous":{"title":"Schema Validation","permalink":"/schema-validation.html"},"next":{"title":"Key Compression","permalink":"/key-compression.html"}}');var i=n(4848),o=n(8453),a=n(3247);const t={title:"Encryption",slug:"encryption.html",description:"Explore RxDB's \ud83d\udd12 encryption plugin for enhanced data security in web and native apps, featuring password-based encryption and secure storage."},l="\ud83d\udd12 Encrypted Local Storage with RxDB",d={},c=[{value:"Querying encrypted data",id:"querying-encrypted-data",level:2},{value:"Password handling",id:"password-handling",level:2},{value:"Asymmetric encryption",id:"asymmetric-encryption",level:2},{value:"Using the RxDB Encryption Plugins",id:"using-the-rxdb-encryption-plugins",level:2},{value:"Wrap your RxStorage with the encryption",id:"wrap-your-rxstorage-with-the-encryption",level:3},{value:"Create a RxDatabase with the wrapped storage",id:"create-a-rxdatabase-with-the-wrapped-storage",level:3},{value:"Create an RxCollection with an encrypted property",id:"create-an-rxcollection-with-an-encrypted-property",level:3},{value:"Using Web-Crypto API",id:"using-web-crypto-api",level:2},{value:"Changing the password",id:"changing-the-password",level:2},{value:"Encrypted attachments",id:"encrypted-attachments",level:2},{value:"Encryption and workers",id:"encryption-and-workers",level:2}];function h(e){const r={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(r.header,{children:(0,i.jsx)(r.h1,{id:"-encrypted-local-storage-with-rxdb",children:"\ud83d\udd12 Encrypted Local Storage with RxDB"})}),"\n",(0,i.jsxs)(r.p,{children:["The RxDB encryption plugin empowers developers to fortify their applications' data security. It seamlessly integrates with ",(0,i.jsx)(r.a,{href:"https://rxdb.info/",children:"RxDB"}),", allowing for the secure storage and retrieval of documents by ",(0,i.jsx)(r.strong,{children:"encrypting them with a password"}),". With encryption and decryption processes handled internally, it ensures that sensitive data remains confidential, making it a valuable tool for building robust, privacy-conscious applications. The encryption works on all RxDB supported devices types like the ",(0,i.jsx)(r.strong,{children:"browser"}),", ",(0,i.jsx)(r.strong,{children:"ReactNative"})," or ",(0,i.jsx)(r.strong,{children:"Node.js"}),"."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/icons/with-gradient/storage-layer.svg",alt:"Encryption Storage Layer",height:"60"})}),"\n",(0,i.jsx)(r.p,{children:"Encrypting client-side stored data in RxDB offers numerous advantages:"}),"\n",(0,i.jsxs)(r.ul,{children:["\n",(0,i.jsxs)(r.li,{children:[(0,i.jsx)(r.strong,{children:"Enhanced Security"}),": In the unfortunate event of a user's device being stolen, the encrypted data remains safeguarded on the hard drive, inaccessible without the correct password."]}),"\n",(0,i.jsxs)(r.li,{children:[(0,i.jsx)(r.strong,{children:"Access Control"}),": You can retain control over stored data by revoking access at any time simply by withholding the password."]}),"\n",(0,i.jsxs)(r.li,{children:[(0,i.jsx)(r.strong,{children:"Tamper proof"})," Other applications on the device cannot read out the stored data when the password is only kept in the process-specific memory"]}),"\n"]}),"\n",(0,i.jsx)(r.h2,{id:"querying-encrypted-data",children:"Querying encrypted data"}),"\n",(0,i.jsxs)(r.p,{children:["RxDB handles the encryption and decryption of data internally. This means that when you work with a RxDocument, you can access the properties of the document just like you would with normal, unencrypted data. RxDB automatically decrypts the data for you when you retrieve it, making it transparent to your application code.\nThis means the encryption works with all ",(0,i.jsx)(r.a,{href:"/rx-storage.html",children:"RxStorage"})," like ",(0,i.jsx)(r.strong,{children:"SQLite"}),", ",(0,i.jsx)(r.strong,{children:"IndexedDB"}),", ",(0,i.jsx)(r.strong,{children:"OPFS"})," and so on."]}),"\n",(0,i.jsxs)(r.p,{children:["However, there's a limitation when it comes to querying encrypted fields. ",(0,i.jsx)(r.strong,{children:"Encrypted fields cannot be used as operators in queries"}),'. This means you cannot perform queries like "find all documents where the encrypted field equals a certain value." RxDB does not expose the encrypted data in a way that allows direct querying based on the encrypted content. To filter or search for documents based on the contents of encrypted fields, you would need to first decrypt the data and then perform the query, which might not be efficient or practical in some cases.\nYou could however use the ',(0,i.jsx)(r.a,{href:"/rx-storage-memory-mapped.html",children:"memory mapped"})," RxStorage to replicate the encrypted documents into a non-encrypted in-memory storage and then query them like normal."]}),"\n",(0,i.jsx)(r.h2,{id:"password-handling",children:"Password handling"}),"\n",(0,i.jsx)(r.p,{children:"RxDB does not define how you should store or retrieve the encryption password. It only requires you to provide the password on database creation which grants you flexibility in how you manage encryption passwords.\nYou could ask the user on app-start to insert the password, or you can retrieve the password from your backend on app start (or revoke access by no longer providing the password)."}),"\n",(0,i.jsx)(r.h2,{id:"asymmetric-encryption",children:"Asymmetric encryption"}),"\n",(0,i.jsxs)(r.p,{children:["The encryption plugin itself uses ",(0,i.jsx)(r.strong,{children:"symmetric encryption"})," with a password to guarantee best performance when reading and storing data.\nIt is not able to do ",(0,i.jsx)(r.strong,{children:"Asymmetric encryption"})," by itself. If you need Asymmetric encryption with a private/publicKey, it is recommended to encrypted the password itself with the asymmetric keys and store the encrypted password beside the other data. On app-start you can decrypt the password with the private key and use the decrypted password in the RxDB encryption plugin"]}),"\n",(0,i.jsx)(r.h2,{id:"using-the-rxdb-encryption-plugins",children:"Using the RxDB Encryption Plugins"}),"\n",(0,i.jsx)(r.p,{children:"RxDB currently has two plugins for encryption:"}),"\n",(0,i.jsxs)(r.ul,{children:["\n",(0,i.jsxs)(r.li,{children:["The free ",(0,i.jsx)(r.code,{children:"encryption-crypto-js"})," plugin that is based on the ",(0,i.jsx)(r.code,{children:"AES"})," algorithm of the ",(0,i.jsx)(r.a,{href:"https://www.npmjs.com/package/crypto-js",children:"crypto-js"})," library"]}),"\n",(0,i.jsxs)(r.li,{children:["The ",(0,i.jsx)(r.a,{href:"/premium/",children:"\ud83d\udc51 premium"})," ",(0,i.jsx)(r.code,{children:"encryption-web-crypto"})," plugin that is based on the native ",(0,i.jsx)(r.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API",children:"Web Crypto API"})," which makes it faster and more secure to use. Document inserts are about 10x faster compared to ",(0,i.jsx)(r.code,{children:"crypto-js"})," and it has a smaller build size because it uses the browsers API instead of bundling an npm module."]}),"\n"]}),"\n",(0,i.jsxs)(r.p,{children:["An RxDB encryption plugin is a wrapper around any other ",(0,i.jsx)(r.a,{href:"/rx-storage.html",children:"RxStorage"}),"."]}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(r.h3,{id:"wrap-your-rxstorage-with-the-encryption",children:"Wrap your RxStorage with the encryption"}),(0,i.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" wrappedKeyEncryptionCryptoJsStorage"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/encryption-crypto-js'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap the normal storage with the encryption plugin"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" encryptedStorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyEncryptionCryptoJsStorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(r.h3,{id:"create-a-rxdatabase-with-the-wrapped-storage",children:"Create a RxDatabase with the wrapped storage"}),(0,i.jsxs)(r.p,{children:["Also you have to set a ",(0,i.jsx)(r.strong,{children:"password"})," when creating the database. The format of the password depends on which encryption plugin is used."]}),(0,i.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// create an encrypted database"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" encryptedStorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'sudoLetMeIn'"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(r.h3,{id:"create-an-rxcollection-with-an-encrypted-property",children:"Create an RxCollection with an encrypted property"}),(0,i.jsxs)(r.p,{children:["To define a field as being encrypted, you have to add it to the ",(0,i.jsx)(r.code,{children:"encrypted"})," fields list in the schema."]}),(0,i.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" schema"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" secret"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" encrypted: ["}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'secret'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" myDocuments"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" schema"})}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"})"})})]})})})]}),"\n",(0,i.jsx)(r.h2,{id:"using-web-crypto-api",children:"Using Web-Crypto API"}),"\n",(0,i.jsxs)(r.p,{children:["For professionals, we have the ",(0,i.jsx)(r.code,{children:"web-crypto"})," ",(0,i.jsx)(r.a,{href:"/premium/",children:"\ud83d\udc51 premium"})," plugin which is faster and more secure:"]}),"\n",(0,i.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" wrappedKeyEncryptionWebCryptoStorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" createPassword"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/encryption-web-crypto'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap the normal storage with the encryption plugin"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" encryptedIndexedDbStorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyEncryptionWebCryptoStorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" myPasswordObject"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" // Algorithm can be oneOf: 'AES-CTR' | 'AES-CBC' | 'AES-GCM'"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" algorithm"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'AES-CTR'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myRandomPasswordWithMin8Length'"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// create an encrypted database"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" encryptedIndexedDbStorage"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" myPasswordObject"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"})})]})})}),"\n",(0,i.jsx)(r.h2,{id:"changing-the-password",children:"Changing the password"}),"\n",(0,i.jsx)(r.p,{children:"The password is set database specific and it is not possible to change the password of a database. Opening an existing database with a different password will throw an error. To change the password you can either:"}),"\n",(0,i.jsxs)(r.ul,{children:["\n",(0,i.jsxs)(r.li,{children:["Use the ",(0,i.jsx)(r.a,{href:"/migration-storage.html",children:"storage migration plugin"})," to migrate the database state into a new database."]}),"\n",(0,i.jsxs)(r.li,{children:["Store a randomly created meta-password in a different RxDatabase as a value of a ",(0,i.jsx)(r.a,{href:"/rx-local-document.html",children:"local document"}),". Encrypt the meta password with the actual user password and read it out before creating the actual database."]}),"\n"]}),"\n",(0,i.jsx)(r.h2,{id:"encrypted-attachments",children:"Encrypted attachments"}),"\n",(0,i.jsxs)(r.p,{children:["To store the ",(0,i.jsx)(r.a,{href:"/rx-attachment.html",children:"attachments"})," data encrypted, you have to set ",(0,i.jsx)(r.code,{children:"encrypted: true"})," in the ",(0,i.jsx)(r.code,{children:"attachments"})," property of the schema."]}),"\n",(0,i.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" mySchema"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(r.span,{"data-line":"",children:[(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" encrypted"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" // if true, the attachment-data will be encrypted with the db-password"})]}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(r.span,{"data-line":"",children:(0,i.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(r.h2,{id:"encryption-and-workers",children:"Encryption and workers"}),"\n",(0,i.jsxs)(r.p,{children:["If you are using ",(0,i.jsx)(r.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage"})," or ",(0,i.jsx)(r.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker RxStorage"})," with encryption, it's recommended to run encryption inside of the worker. Encryption can be very cpu intensive and would take away CPU-power from the main thread which is the main reason to use workers."]}),"\n",(0,i.jsx)(r.p,{children:"You do not need to worry about setting the password inside of the worker. The password will be set when calling createRxDatabase from the main thread, and will be passed internally to the storage in the worker automatically."})]})}function p(e={}){const{wrapper:r}={...(0,o.R)(),...e.components};return r?(0,i.jsx)(r,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/0596642b.eee766f7.js b/docs/assets/js/0596642b.eee766f7.js
deleted file mode 100644
index c8291a458c8..00000000000
--- a/docs/assets/js/0596642b.eee766f7.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7537],{7196(e,i,t){t.r(i),t.d(i,{default:()=>n});var r=t(4586),s=t(8711),c=t(6540),l=t(8141),d=t(4848);function n(){const{siteConfig:e}=(0,r.A)();return(0,c.useEffect)(()=>{(0,l.c)("paid-meeting-link-clicked-starter-pack",100,1)}),(0,d.jsx)(s.A,{title:`Meeting - ${e.title}`,description:"RxDB Meeting Scheduler",children:(0,d.jsx)("main",{children:(0,d.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,d.jsx)("a",{href:"/",children:(0,d.jsx)("div",{className:"logo",children:(0,d.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,d.jsx)("h1",{children:"RxDB Meeting Scheduler"}),(0,d.jsx)("p",{children:(0,d.jsx)("b",{children:"You will be redirected in a few seconds."})}),(0,d.jsx)("p",{children:(0,d.jsx)("a",{href:"https://rxdb.pipedrive.com/scheduler/LzExD5CX/meeting-starter-pack",children:"Click here"})}),(0,d.jsx)("meta",{httpEquiv:"Refresh",content:"0; url=https://rxdb.pipedrive.com/scheduler/LzExD5CX/meeting-starter-pack"})]})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/08ff000c.3fc1a455.js b/docs/assets/js/08ff000c.3fc1a455.js
deleted file mode 100644
index eb9b5b29df9..00000000000
--- a/docs/assets/js/08ff000c.3fc1a455.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3916],{8453(e,n,t){t.d(n,{R:()=>o,x:()=>l});var i=t(6540);const r={},s=i.createContext(r);function o(e){const n=i.useContext(s);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),i.createElement(s.Provider,{value:n},e.children)}},9918(e,n,t){t.r(n),t.d(n,{assets:()=>a,contentTitle:()=>l,default:()=>c,frontMatter:()=>o,metadata:()=>i,toc:()=>d});const i=JSON.parse('{"id":"releases/16.0.0","title":"RxDB 16.0.0 - Efficiency Redefined","description":"Meet RxDB 16.0.0 - major refactorings, improved performance, and easy migrations. Experience a better, faster, and more stable database solution today.","source":"@site/docs/releases/16.0.0.md","sourceDirName":"releases","slug":"/releases/16.0.0.html","permalink":"/releases/16.0.0.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB 16.0.0 - Efficiency Redefined","slug":"16.0.0.html","description":"Meet RxDB 16.0.0 - major refactorings, improved performance, and easy migrations. Experience a better, faster, and more stable database solution today."},"sidebar":"tutorialSidebar","previous":{"title":"17.0.0","permalink":"/releases/17.0.0.html"},"next":{"title":"15.0.0","permalink":"/releases/15.0.0.html"}}');var r=t(4848),s=t(8453);const o={title:"RxDB 16.0.0 - Efficiency Redefined",slug:"16.0.0.html",description:"Meet RxDB 16.0.0 - major refactorings, improved performance, and easy migrations. Experience a better, faster, and more stable database solution today."},l="16.0.0",a={},d=[{value:"Breaking Changes",id:"breaking-changes",level:2},{value:"Removed deprecated LokiJS RxStorage",id:"removed-deprecated-lokijs-rxstorage",level:3},{value:"Renamed .destroy() to .close()",id:"renamed-destroy-to-close",level:3},{value:"ignoreDuplicate: true on createRxDatabase() must only be allowed in dev-mode.",id:"ignoreduplicate-true-on-createrxdatabase-must-only-be-allowed-in-dev-mode",level:3},{value:"When dev-mode is enabled, a schema validator must be used.",id:"when-dev-mode-is-enabled-a-schema-validator-must-be-used",level:3},{value:"Removed the memory-synced storage",id:"removed-the-memory-synced-storage",level:3},{value:"Split conflict handler functionality into isEqual() and resolve().",id:"split-conflict-handler-functionality-into-isequal-and-resolve",level:3},{value:"Full rewrite of the OPFS and Filesystem-Node RxStorages",id:"full-rewrite-of-the-opfs-and-filesystem-node-rxstorages",level:2},{value:"Internal Changes",id:"internal-changes",level:2},{value:"Bugfixes",id:"bugfixes",level:2},{value:"Other",id:"other",level:2},{value:"You can help!",id:"you-can-help",level:2},{value:"LinkedIn",id:"linkedin",level:2}];function h(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"1600",children:"16.0.0"})}),"\n",(0,r.jsxs)(n.p,{children:["The release ",(0,r.jsx)(n.a,{href:"https://rxdb.info/releases/16.0.0.html",children:"16.0.0"})," is used for major refactorings. We did not change that much, mostly renaming things and fixing confusing implementation details."]}),"\n",(0,r.jsxs)(n.p,{children:["Data stored in the previous version ",(0,r.jsx)(n.code,{children:"15"})," is compatible with the code of the new version ",(0,r.jsx)(n.code,{children:"16"})," for most RxStorage implementation. So migration will be easy.\nOnly the following RxStorage implementations are required to migrate the data itself with the ",(0,r.jsx)(n.a,{href:"/migration-storage.html",children:"storage migration plugin"}),":"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"SQLite RxStorage"}),"\n",(0,r.jsx)(n.li,{children:"NodeFilesystem RxStorage"}),"\n",(0,r.jsx)(n.li,{children:"OPFS RxStorage"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"breaking-changes",children:"Breaking Changes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["CHANGE ",(0,r.jsx)(n.a,{href:"https://rxdb.info/rx-server.html",children:"RxServer"})," is no longer in beta mode."]}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE ",(0,r.jsx)(n.a,{href:"https://rxdb.info/fulltext-search.html",children:"Fulltext Search"})," is no longer in beta mode."]}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE ",(0,r.jsx)(n.a,{href:"https://rxdb.info/reactivity.html",children:"Custom Reactivity"})," is no longer in beta mode."]}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE ",(0,r.jsx)(n.a,{href:"https://rxdb.info/replication.html",children:"initialCheckpoint in replications"})," is no longer in beta mode."]}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE ",(0,r.jsx)(n.a,{href:"https://rxdb.info/rx-state.html",children:"RxState"})," is no longer in beta mode."]}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE ",(0,r.jsx)(n.a,{href:"https://rxdb.info/rx-storage-memory-mapped.html",children:"MemoryMapped RxStorage"})," is no longer in beta mode."]}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE rename ",(0,r.jsx)(n.code,{children:"randomCouchString()"})," to ",(0,r.jsx)(n.code,{children:"randomToken()"})]}),"\n",(0,r.jsxs)(n.li,{children:["FIX (GraphQL replication) datapath must be equivalent for pull and push ",(0,r.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/pull/6019",children:"#6019"})]}),"\n",(0,r.jsxs)(n.li,{children:["REMOVE fallback to the ",(0,r.jsx)(n.code,{children:"ohash"})," package when ",(0,r.jsx)(n.code,{children:"crypto.subtle"})," does not exist. All modern runtimes (also react-native) now support ",(0,r.jsx)(n.code,{children:"crypto.subtle"}),", so we do not need that fallback anymore."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"removed-deprecated-lokijs-rxstorage",children:"Removed deprecated LokiJS RxStorage"}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.a,{href:"https://rxdb.info/rx-storage-lokijs.html",children:"LokiJS RxStorage"})," was deprecated because LokiJS itself is no longer maintained. Therefore it will be removed completely. If you still need that, you can fork the code of it and publish it in an own package and link to it from the ",(0,r.jsx)(n.a,{href:"https://rxdb.info/third-party-plugins.html",children:"third party plugins page"}),"."]}),"\n",(0,r.jsxs)(n.h3,{id:"renamed-destroy-to-close",children:["Renamed ",(0,r.jsx)(n.code,{children:".destroy()"})," to ",(0,r.jsx)(n.code,{children:".close()"})]}),"\n",(0,r.jsxs)(n.p,{children:["Destroy was adapted from PouchDB, but people often think this deletes the written data. ",(0,r.jsx)(n.code,{children:"close"})," is a better name for that functionality."]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Also renamed similar functions/attributes:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".destroy()"})," to ",(0,r.jsx)(n.code,{children:".close()"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".onDestroy()"})," to ",(0,r.jsx)(n.code,{children:".onClose()"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"postDestroyRxCollection"})," to ",(0,r.jsx)(n.code,{children:"postCloseRxCollection"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"preDestroyRxDatabase"})," to ",(0,r.jsx)(n.code,{children:"preCloseRxDatabase"})]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.h3,{id:"ignoreduplicate-true-on-createrxdatabase-must-only-be-allowed-in-dev-mode",children:[(0,r.jsx)(n.code,{children:"ignoreDuplicate: true"})," on ",(0,r.jsx)(n.code,{children:"createRxDatabase()"})," must only be allowed in dev-mode."]}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.code,{children:"ignoreDuplicate"})," flag is only useful for tests and should never be used in production. We now throw an error if it is set to ",(0,r.jsx)(n.code,{children:"true"})," in non-dev-mode."]}),"\n",(0,r.jsx)(n.h3,{id:"when-dev-mode-is-enabled-a-schema-validator-must-be-used",children:"When dev-mode is enabled, a schema validator must be used."}),"\n",(0,r.jsx)(n.p,{children:"Many reported issues come from people storing data that is not valid to their schema.\nTo fix this, in dev-mode it is now required that at least one schema validator is used."}),"\n",(0,r.jsx)(n.h3,{id:"removed-the-memory-synced-storage",children:"Removed the memory-synced storage"}),"\n",(0,r.jsxs)(n.p,{children:["The memory-synced RxStorage was removed in RxDB version 16. Please use the ",(0,r.jsx)(n.code,{children:"memory-mapped"})," storage instead which has better trade-offs and is easier to configure."]}),"\n",(0,r.jsxs)(n.h3,{id:"split-conflict-handler-functionality-into-isequal-and-resolve",children:["Split conflict handler functionality into ",(0,r.jsx)(n.code,{children:"isEqual()"})," and ",(0,r.jsx)(n.code,{children:"resolve()"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"Because the handler is used in so many places it becomes confusing to write a proper conflict handler.\nAlso having a handler that requires user interaction is only possible by hackingly using the context param.\nBy splitting the functionalities it is easier to learn where the handlers are used and how to define them properly."}),"\n",(0,r.jsx)(n.h2,{id:"full-rewrite-of-the-opfs-and-filesystem-node-rxstorages",children:"Full rewrite of the OPFS and Filesystem-Node RxStorages"}),"\n",(0,r.jsxs)(n.p,{children:["The ",(0,r.jsx)(n.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," and ",(0,r.jsx)(n.a,{href:"/rx-storage-filesystem-node.html",children:"Filesystem-Node"})," RxStorage had problems with storing emojis and other special characters inside of indexed fields.\nI completely rewrote them and improved performance especially on initial load when a lot of data is stored already and when doing many small writes/reads at the same time on in series."]}),"\n",(0,r.jsx)(n.h2,{id:"internal-changes",children:"Internal Changes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"CHANGE (internal) migration-storage plugin: Remove catch from cleanup"}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE (internal) rename RX_PIPELINE_CHECKPOINT_CONTEXT to ",(0,r.jsx)(n.code,{children:"rx-pipeline-checkpoint"})]}),"\n",(0,r.jsxs)(n.li,{children:["CHANGE (internal) remove ",(0,r.jsx)(n.code,{children:"conflictResultionTasks()"})," and ",(0,r.jsx)(n.code,{children:"resolveConflictResultionTask()"})," from the RxStorage interface."]}),"\n",(0,r.jsx)(n.li,{children:"REMOVED (internal) do not check for duplicate event bulks, all RxStorage implementations must guarantee to not emit the same events twice."}),"\n",(0,r.jsx)(n.li,{children:"REFACTOR (internal) Only use event-bulks internally and only transform to single emitted events if actually someone has subscribed to the eventstream."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"bugfixes",children:"Bugfixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Having a lot of documents pulled in the replication could in some cases slow down the database initialization because ",(0,r.jsx)(n.code,{children:"upstreamInitialSync()"})," did not set a checkpoint and each time checked all documents if they are equal to the master."]}),"\n",(0,r.jsxs)(n.li,{children:["If the handler of a ",(0,r.jsx)(n.a,{href:"/rx-pipeline.html",children:"RxPipeline"})," throws an error, block the whole pipeline and emit the error to the outside."]}),"\n",(0,r.jsxs)(n.li,{children:["Throw error when dexie.js RxStorage is used with optional index fields ",(0,r.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/pull/6643#issuecomment-2505310082",children:"#6643"}),"."]}),"\n",(0,r.jsx)(n.li,{children:"Fix IndexedDB bug: Some people had problems with the IndexedDB RxStorage that opened up collections very slowly. If you had this problem, please try out this new version."}),"\n",(0,r.jsx)(n.li,{children:"Add check to ensure remote instances are build with the same RxDB version. This is to ensure if you update RxDB and forget to rebuild your workers, it will throw instead of causing strange problems."}),"\n",(0,r.jsx)(n.li,{children:"When the pulled documents in the replication do not match the schema, do not update the checkpoint."}),"\n",(0,r.jsx)(n.li,{children:"When the pushed document conflict results in the replication do not match the schema, do not update the checkpoint."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"other",children:"Other"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Added more ",(0,r.jsx)(n.a,{href:"https://rxdb.info/rx-storage-performance.html",children:"performance tests"})]}),"\n",(0,r.jsxs)(n.li,{children:["The amount of collections in the open source version has been limited to ",(0,r.jsx)(n.code,{children:"16"}),"."]}),"\n",(0,r.jsx)(n.li,{children:"Moved RxQuery checks into dev-mode."}),"\n",(0,r.jsx)(n.li,{children:"RxQuery.remove() now internally does a bulk operation for better performance."}),"\n",(0,r.jsx)(n.li,{children:"Lazily process bulkWrite() results for less CPU usage."}),"\n",(0,r.jsx)(n.li,{children:"Only run interval cleanup on the storage of a collection if there actually have been writes to it."}),"\n",(0,r.jsxs)(n.li,{children:["Schema validation errors (code: 422) now include the ",(0,r.jsx)(n.code,{children:"RxJsonSchema"})," for easier debugging."]}),"\n",(0,r.jsx)(n.li,{children:"Added an interval to prevent browser tab hibernation while a replication is running."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"you-can-help",children:"You can help!"}),"\n",(0,r.jsxs)(n.p,{children:["There are many things that can be done by ",(0,r.jsx)(n.strong,{children:"you"})," to improve RxDB:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Check the ",(0,r.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/BACKLOG.md",children:"BACKLOG"})," for features that would be great to have."]}),"\n",(0,r.jsxs)(n.li,{children:["Check the ",(0,r.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/before-next-major.md",children:"breaking backlog"})," for breaking changes that must be implemented in the future but where I did not have the time yet."]}),"\n",(0,r.jsxs)(n.li,{children:["Check the ",(0,r.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/search?q=todo",children:"todos"})," in the code. There are many small improvements that can be done for performance and build size."]}),"\n",(0,r.jsx)(n.li,{children:"Review the code and add tests. I am only a single human with a laptop. My code is not perfect and much small improvements can be done when people review the code and help me to clarify undefined behaviors."}),"\n",(0,r.jsxs)(n.li,{children:["Update the ",(0,r.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"example projects"})," some of them are outdated and need updates."]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"linkedin",children:"LinkedIn"}),"\n",(0,r.jsxs)(n.p,{children:["Stay connected with the latest updates and network with professionals in the RxDB community by following RxDB's ",(0,r.jsx)(n.a,{href:"https://www.linkedin.com/company/rxdb",children:"official LinkedIn page"}),"!"]})]})}function c(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/0b761dc7.ea7a941e.js b/docs/assets/js/0b761dc7.ea7a941e.js
deleted file mode 100644
index 10818e4143f..00000000000
--- a/docs/assets/js/0b761dc7.ea7a941e.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2835],{2796(e,a,t){t.r(a),t.d(a,{default:()=>n});var r=t(544),s=t(4848);function n(){return(0,r.default)({sem:{id:"gads",metaTitle:"The best Database on top of localstorage",appName:"Browser",title:(0,s.jsxs)(s.Fragment,{children:["The easiest way to ",(0,s.jsx)("b",{children:"store"})," and ",(0,s.jsx)("b",{children:"sync"})," Data in LocalStorage"]}),text:(0,s.jsx)(s.Fragment,{children:"Store data inside the Browsers localstorage to build high performance realtime applications that sync data from the backend and even work when offline."})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/0e268d20.8eaca8eb.js b/docs/assets/js/0e268d20.8eaca8eb.js
deleted file mode 100644
index 61227d2fec1..00000000000
--- a/docs/assets/js/0e268d20.8eaca8eb.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2584],{686(e,r,a){function i(e,r){if(e===r)return!0;if(e&&r&&"object"==typeof e&&"object"==typeof r){if(e.constructor!==r.constructor)return!1;var a,s;if(Array.isArray(e)){if((a=e.length)!==r.length)return!1;for(s=a;0!==s--;)if(!i(e[s],r[s]))return!1;return!0}if(e.constructor===RegExp)return e.source===r.source&&e.flags===r.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===r.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===r.toString();var t=Object.keys(e);if((a=t.length)!==Object.keys(r).length)return!1;for(s=a;0!==s--;)if(!Object.prototype.hasOwnProperty.call(r,t[s]))return!1;for(s=a;0!==s--;){var n=t[s];if(!i(e[n],r[n]))return!1}return!0}return e!=e&&r!=r}a.d(r,{b:()=>i})},4370(e,r,a){a.d(r,{_:()=>s});var i=a(4629);function s(e,r,a,i,s){return new t(e,r,a,i,s)}var t=function(e){function r(r,a,i,s,t,n){var l=e.call(this,r)||this;return l.onFinalize=t,l.shouldUnsubscribe=n,l._next=a?function(e){try{a(e)}catch(i){r.error(i)}}:e.prototype._next,l._error=s?function(e){try{s(e)}catch(e){r.error(e)}finally{this.unsubscribe()}}:e.prototype._error,l._complete=i?function(){try{i()}catch(e){r.error(e)}finally{this.unsubscribe()}}:e.prototype._complete,l}return(0,i.C6)(r,e),r.prototype.unsubscribe=function(){var r;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){var a=this.closed;e.prototype.unsubscribe.call(this),!a&&(null===(r=this.onFinalize)||void 0===r||r.call(this))}},r}(a(8857).vU)},5096(e,r,a){a.d(r,{N:()=>s});var i=a(1693);function s(e){return function(r){if(function(e){return(0,i.T)(null==e?void 0:e.lift)}(r))return r.lift(function(r){try{return e(r,this)}catch(a){this.error(a)}});throw new TypeError("Unable to lift unknown Observable type")}}},5520(e,r,a){a.d(r,{F:()=>n});var i=a(3887),s=a(5096),t=a(4370);function n(e,r){return void 0===r&&(r=i.D),e=null!=e?e:l,(0,s.N)(function(a,i){var s,n=!0;a.subscribe((0,t._)(i,function(a){var t=r(a);!n&&e(s,t)||(n=!1,s=t,i.next(a))}))})}function l(e,r){return e===r}},7708(e,r,a){a.d(r,{T:()=>t});var i=a(5096),s=a(4370);function t(e,r){return(0,i.N)(function(a,i){var t=0;a.subscribe((0,s._)(i,function(a){i.next(e.call(r,a,t++))}))})}},9561(e,r,a){a.r(r),a.d(r,{FORM_VALUE_DOCUMENT_ID:()=>j,TEAM_SIZES:()=>f,default:()=>v});var i=a(4586),s=a(8711),t=a(5260),n=a(6540),l=a(686),o=a(3337),c=a(9855),d=a(2303),h=a(5520),m=a(7708),u=a(8141),p=a(3943),x=a(4584),g=a(4848);const j="premium-price-form",f=[1,3,6,12,24,30];let b;function y(){return b||(b=(async()=>{const e=await Promise.all([a.e(9850),a.e(4291)]).then(a.bind(a,4291)),r=await e.getDatabase();let i=await r.getLocal(j);return i||(i=await r.upsertLocal(j,{formSubmitted:!1,developers:f[1],packages:["browser"]})),i})()),b}function k(e){return(0,g.jsx)("input",{name:"package-"+e.packageName,type:"checkbox",className:"package-checkbox",checked:!!e.formValue?.packages.includes(e.packageName),readOnly:!0,onClick:()=>{(0,u.c)("calculate_premium_price",3,1,!0),e.onToggle()}})}function v(){const{siteConfig:e}=(0,i.A)(),r=(0,d.A)(),[a,p]=n.useState(!1),[j,f]=n.useState(null);(0,n.useEffect)(()=>{r&&(a||(p(!0),r&&(0,u.c)("open_pricing_page",1),(async()=>{(await y()).$.pipe((0,m.T)(e=>e._data.data),(0,h.F)(l.b)).subscribe(e=>{console.log("XXX new data:"),console.dir(e),f(e),async function(){const e=await y(),r=(0,c.Pu)(e);console.log("priceResult:"),console.log(JSON.stringify(r,null,4));const a=(0,o.ZN)(document.getElementById("price-calculator-result")),i=(0,o.ZN)(document.getElementById("total-per-project-per-month")),s=(0,o.ZN)(document.getElementById("total-per-project-per-year"));t=r.totalPrice,console.log("setPrice:"),console.dir(t),i.innerHTML=Math.ceil(t/12).toString(),s.innerHTML=Math.ceil(t).toString(),a.style.display="block";var t}()})})()))});const[b,v]=n.useState(!1),N=()=>{(0,u.c)("open_premium_submit_popup",20,1),v(!0)};function S(e){return y().then(r=>r.incrementalModify(r=>(r.packages.includes(e)?r.packages=r.packages.filter(r=>r!==e):r.packages.push(e),r)))}return(0,g.jsxs)(g.Fragment,{children:[(0,g.jsxs)(t.A,{children:[(0,g.jsx)("body",{className:"homepage"}),(0,g.jsx)("link",{rel:"canonical",href:"/premium/"})]}),(0,g.jsx)(s.A,{title:`Premium Plugins - ${e.title}`,description:"RxDB plugins for professionals. FAQ, pricing and license",children:(0,g.jsxs)("main",{children:[(0,g.jsx)("div",{className:"block first",children:(0,g.jsxs)("div",{className:"content centered",children:[(0,g.jsxs)("h2",{children:["RxDB ",(0,g.jsx)("b",{children:"Premium"})]}),(0,g.jsxs)("p",{style:{width:"80%"},children:["RxDB's Premium plugins offer advanced features and performance improvements designed for businesses and professionals. They are ideal for commercial or critical projects, providing ",(0,g.jsx)("a",{href:"/rx-storage-performance.html",target:"_blank",children:"better performance"}),", a smaller build size, flexible storage engines, secure encryption and other features."]})]})}),(0,g.jsx)("div",{className:"block dark",id:"price-calculator-block",children:(0,g.jsxs)("div",{className:"content centered",children:[(0,g.jsx)("h2",{children:"Price Calculator"}),(0,g.jsx)("div",{className:"price-calculator",children:(0,g.jsx)("div",{className:"price-calculator-inner",children:(0,g.jsx)("form",{id:"price-calculator-form",children:(0,g.jsxs)("div",{className:"packages",children:[(0,g.jsx)("h3",{children:"Choose your Packages"}),(0,g.jsx)("div",{className:"package",children:(0,g.jsxs)("div",{className:"package-inner",children:[(0,g.jsx)(k,{packageName:"browser",onToggle:()=>S("browser"),formValue:j}),(0,g.jsx)("h4",{children:"Browser Package"}),(0,g.jsxs)("ul",{children:[(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-opfs.html",target:"_blank",children:"RxStorage OPFS"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-indexeddb.html",target:"_blank",children:"RxStorage IndexedDB"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-worker.html",target:"_blank",children:"RxStorage Worker"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/encryption.html",target:"_blank",children:"WebCrypto Encryption"})})]})]})}),(0,g.jsx)("div",{className:"package",children:(0,g.jsxs)("div",{className:"package-inner",children:[(0,g.jsx)(k,{packageName:"native",onToggle:()=>S("native"),formValue:j}),(0,g.jsx)("h4",{children:"Native Package"}),(0,g.jsxs)("ul",{children:[(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-sqlite.html",target:"_blank",children:"RxStorage SQLite"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-filesystem-node.html",target:"_blank",children:"RxStorage Filesystem Node"})})]})]})}),(0,g.jsx)("div",{className:"package",children:(0,g.jsxs)("div",{className:"package-inner",children:[(0,g.jsx)(k,{packageName:"performance",onToggle:()=>S("performance"),formValue:j}),(0,g.jsx)("h4",{children:"Performance Package"}),(0,g.jsxs)("ul",{children:[(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-sharding.html",target:"_blank",children:"RxStorage Sharding"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-memory-mapped.html",target:"_blank",children:"RxStorage Memory Mapped"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/query-optimizer.html",target:"_blank",children:"Query Optimizer"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-localstorage-meta-optimizer.html",target:"_blank",children:"RxStorage Localstorage Meta Optimizer"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-storage-shared-worker.html",target:"_blank",children:"RxStorage Shared Worker"})})]})]})}),(0,g.jsx)("div",{className:"package",children:(0,g.jsxs)("div",{className:"package-inner",children:[(0,g.jsx)(k,{packageName:"server",onToggle:()=>S("server"),formValue:j}),(0,g.jsx)("h4",{children:"Server Package"}),(0,g.jsxs)("ul",{children:[(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-server.html",target:"_blank",children:"RxServer Adapter Fastify"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/rx-server.html",target:"_blank",children:"RxServer Adapter Koa"})})]})]})}),(0,g.jsx)("div",{className:"package",children:(0,g.jsxs)("div",{className:"package-inner",children:[(0,g.jsx)("input",{name:"package-utilities",type:"checkbox",className:"package-checkbox",defaultChecked:!0,disabled:!0}),(0,g.jsxs)("h4",{children:["Utilities Package ",(0,g.jsx)("b",{children:"(always included)"})]}),(0,g.jsxs)("ul",{children:[(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/logger.html",target:"_blank",children:"Logger"})}),(0,g.jsx)("li",{children:(0,g.jsx)("a",{href:"/fulltext-search.html",target:"_blank",children:"Fulltext Search"})})]})]})}),(0,g.jsx)("div",{className:"clear"}),(0,g.jsx)("div",{className:"clear"})]})})})}),(0,g.jsx)("div",{className:"price-calculator",id:"price-calculator-result",style:{marginBottom:90,display:"none"},children:(0,g.jsxs)("div",{className:"price-calculator-inner",children:[(0,g.jsx)("h4",{children:"Calculated Price:"}),(0,g.jsxs)("div",{className:"inner",children:[(0,g.jsx)("span",{className:"price-label",children:"\u20ac"}),(0,g.jsx)("span",{id:"total-per-project-per-month",children:"XX"}),(0,g.jsx)("span",{className:"per-month",children:"/month"}),(0,g.jsx)("span",{className:"clear"})]}),(0,g.jsxs)("div",{className:"inner",children:["(billed yearly: ",(0,g.jsx)("span",{id:"total-per-project-per-year"})," \u20ac)"]}),(0,g.jsx)("br",{}),(0,g.jsx)("div",{className:"clear"}),(0,g.jsx)(x.$,{primary:!0,style:{width:"100%"},onClick:N,children:"Buy Now \xbb"}),(0,g.jsxs)("div",{style:{fontSize:"70%",textAlign:"center",marginTop:16},children:["If you have any questions, see the FAQ below or fill out the ",(0,g.jsx)("b",{style:{color:"white",textDecoration:"underline",cursor:"pointer"},onClick:N,children:"Buy-Now Form"})," to get in contact."]}),(0,g.jsx)("div",{className:"clear"})]})})]})}),(0,g.jsx)("div",{className:"block",id:"faq",children:(0,g.jsxs)("div",{className:"content centered premium-faq",children:[(0,g.jsxs)("h2",{children:["F.A.Q. ",(0,g.jsx)("b",{children:"(click to toggle)"})]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"What is the process for making a purchase?"}),(0,g.jsxs)("ul",{children:[(0,g.jsxs)("li",{children:["Fill out the ",(0,g.jsx)("b",{style:{color:"white",textDecoration:"underline",cursor:"pointer"},onClick:N,children:"Buy now form"}),"."]}),(0,g.jsx)("li",{children:"You will get a license agreement that you can sign online."}),(0,g.jsx)("li",{children:"You will get an invoice via stripe.com."}),(0,g.jsxs)("li",{children:["After payment you get the access token that you can use to add the Premium plugins to your project with ",(0,g.jsx)("a",{href:"https://www.npmjs.com/package/rxdb-premium",target:"_blank",children:"these instructions"}),"."]})]})]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"Do I need the Premium Plugins?"}),"RxDB Core is open source and many use cases can be implemented with the Open Core part of RxDB. There are many"," ",(0,g.jsx)("a",{href:"/rx-storage.html",target:"_blank",children:"RxStorage"})," ","options and all core plugins that are required for replication, schema validation, encryption and so on, are totally free. As soon as your application is more than a side project you can consider using the premium plugins as an easy way to improve your applications performance and reduce the build size.",(0,g.jsx)("br",{}),"The main benefit of the Premium Plugins is ",(0,g.jsx)("b",{children:"performance"}),". The Premium RxStorage implementations have a better performance so reading and writing data is much faster especially on low-end devices. You can find a performance comparison"," ",(0,g.jsx)("a",{href:"/rx-storage-performance.html",target:"_blank",children:"here"}),". Also there are additional Premium Plugins that can be used to further optimize the performance of your application like the"," ",(0,g.jsx)("a",{href:"/query-optimizer.html",target:"_blank",children:"Query Optimizer"})," ","or the"," ",(0,g.jsx)("a",{href:"/rx-storage-sharding.html",target:"_blank",children:"Sharding"})," ","plugin."]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"Can I get a free trial period?"}),(0,g.jsxs)("ul",{children:[(0,g.jsx)("li",{children:"We do not currently offer a free trial. Instead, we encourage you to explore RxDB's open-source core to evaluate the technology before purchasing the Premium Plugins."}),(0,g.jsxs)("li",{children:["Access to the Premium Plugins requires a signed licensing agreement, which safeguards both parties but also adds administrative overhead. For this reason, we cannot offer free trials or monthly subscriptions; Premium licenses are provided on an ",(0,g.jsx)("b",{children:"annual basis"}),"."]})]})]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"Can I install/build the premium plugins in my CI?"}),"Yes, you can safely install and use the Premium Plugins in your CI without additional payment."]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"Which payment methods are accepted?"}),(0,g.jsx)("b",{children:"Stripe.com"})," is used as payment processor so most known payment options like credit card, PayPal, SEPA transfer and others are available. A list of all options can be found"," ",(0,g.jsx)("a",{href:"https://stripe.com/docs/payments/payment-methods/overview",title:"stripe payment options",target:"_blank",children:"here"}),"."]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"Is there any tracking code inside of the premium plugins?"}),"No, the premium plugins themself do not contain any tracking code. When you build your application with RxDB and deploy it to production, it will not make requests from your users to any RxDB server."]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"Can I add more Premium Packages later if I've already purchased some?"}),"Yes! You can upgrade or add additional Premium Packages at any time. When you decide to purchase more, we'll calculate a fair upgrade price, meaning you only pay the difference between your existing purchase and the new package total.",(0,g.jsx)("br",{}),"Your previous payments are fully credited, so you never pay twice for the same plugins."]}),(0,g.jsxs)("details",{children:[(0,g.jsx)("summary",{children:"Can I get a discount?"}),"There are multiple ways to get a discount:",(0,g.jsxs)("ul",{children:[(0,g.jsxs)("li",{children:[(0,g.jsx)("h5",{children:"Contribute to the RxDB github repository"}),(0,g.jsx)("p",{children:"If you have made significant contributions to the RxDB github repository, you can apply for a discount depending on your contribution."})]}),(0,g.jsxs)("li",{children:[(0,g.jsx)("h5",{children:"Get 25% off by writing about how you use RxDB"}),(0,g.jsx)("p",{children:"On your company/project website, publish an article/blogpost about how you use RxDB in your project. Include how your setup looks like, how you use RxDB in that setup and what problems you had and how did you overcome them. You also need to link to the RxDB website or documentation pages."})]}),(0,g.jsxs)("li",{children:[(0,g.jsx)("h5",{children:"Be active in the RxDB community"}),(0,g.jsx)("p",{children:"If you are active in the RxDB community and discord channel by helping others out or creating educational content like videos and tutorials, feel free to apply for a discount."})]}),(0,g.jsxs)("li",{children:[(0,g.jsx)("h5",{children:"Solve one of the free-premium-tasks"}),(0,g.jsxs)("p",{children:["For private personal projects there is the option to solve one of the"," ",(0,g.jsx)("a",{href:"https://github.com/pubkey/rxdb/blob/master/orga/premium-tasks.md",target:"_blank",children:"Premium Tasks"})," ","to get a free 2 years access to the Premium Plugins."]})]})]})]})]})}),(0,g.jsx)("div",{className:"block dark",children:(0,g.jsxs)("div",{className:"content centered",children:[(0,g.jsxs)("h2",{children:["RxDB Premium Plugins ",(0,g.jsx)("b",{children:"Overview"})]}),(0,g.jsxs)("div",{className:"premium-blocks",children:[(0,g.jsx)("a",{href:"/rx-storage-indexeddb.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage IndexedDB"}),(0,g.jsxs)("p",{children:["A storage for browsers based on ",(0,g.jsx)("b",{children:"IndexedDB"}),". Has the best latency on writes and smallest build size."]})]})})}),(0,g.jsx)("a",{href:"/rx-storage-opfs.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-left-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage OPFS"}),(0,g.jsxs)("p",{children:["Currently the RxStorage with the best data throughput that can be used in the browser. Based on the ",(0,g.jsx)("b",{children:"OPFS File System Access API"}),"."]})]})})}),(0,g.jsx)("a",{href:"/rx-storage-sqlite.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage SQLite"}),(0,g.jsxs)("p",{children:["A fast storage based on ",(0,g.jsx)("b",{children:"SQLite"})," for Servers and Hybrid Apps. Can be used with"," ",(0,g.jsx)("b",{children:"Node.js"}),", ",(0,g.jsx)("b",{children:"Electron"}),", ",(0,g.jsx)("b",{children:"React Native"}),", ",(0,g.jsx)("b",{children:"Capacitor"}),"."]})]})})}),(0,g.jsx)("a",{href:"/rx-storage-shared-worker.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-left-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage SharedWorker"}),(0,g.jsxs)("p",{children:["A RxStorage wrapper to run the storage inside of a SharedWorker which improves the performance by taking CPU load away from the main process. Used in ",(0,g.jsx)("b",{children:"browsers"}),"."]})]})})}),(0,g.jsx)("a",{href:"/rx-storage-worker.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-left-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage Worker"}),(0,g.jsx)("p",{children:"A RxStorage wrapper to run the storage inside of a Worker which improves the performance by taking CPU load away from the main process."})]})})}),(0,g.jsx)("a",{href:"/rx-storage-sharding.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage Sharding"}),(0,g.jsx)("p",{children:"A wrapper around any other storage that improves performance by applying the sharding technique."})]})})}),(0,g.jsx)("a",{href:"/rx-storage-memory-mapped.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-left-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage Memory Mapped"}),(0,g.jsx)("p",{children:"A wrapper around any other storage that creates a mapped in-memory copy which improves performance for the initial page load time and write & read operations."})]})})}),(0,g.jsx)("a",{href:"/query-optimizer.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"Query Optimizer"}),(0,g.jsx)("p",{children:"A tool to find the best index for a given query. You can use this during build time to find the best index and then use that index during runtime."})]})})}),(0,g.jsx)("a",{href:"/rx-storage-localstorage-meta-optimizer.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-left-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage Localstorage Meta Optimizer"}),(0,g.jsxs)("p",{children:["A wrapper around any other storage which optimizes the initial page load one by using localstorage for meta key-value document. Only works in ",(0,g.jsx)("b",{children:"browsers"}),"."]})]})})}),(0,g.jsx)("a",{href:"/encryption.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"WebCrypto Encryption"}),(0,g.jsx)("p",{children:"A faster and more secure encryption plugin based on the Web Crypto API."})]})})}),(0,g.jsx)("a",{href:"/rx-storage-filesystem-node.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-left-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxStorage Filesystem Node"}),(0,g.jsxs)("p",{children:["A fast RxStorage based on the ",(0,g.jsx)("b",{children:"Node.js"})," Filesystem."]})]})})}),(0,g.jsx)("a",{href:"/logger.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"Logger"}),(0,g.jsx)("p",{children:"A logging plugin useful to debug performance problems and for monitoring with Application Performance Monitoring (APM) tools like Bugsnag, Datadog, Elastic, Sentry and others"})]})})}),(0,g.jsx)("a",{href:"/rx-server.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-left-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxServer Fastify Adapter"}),(0,g.jsx)("p",{children:"An adapter to use the RxServer with fastify instead of express. Used to have better performance when serving requests."})]})})}),(0,g.jsx)("a",{href:"/logger.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"RxServer Koa Adapter"}),(0,g.jsx)("p",{children:"An adapter to use the RxServer with Koa instead of express. Used to have better performance when serving requests."})]})})}),(0,g.jsx)("a",{href:"/fulltext-search.html",target:"_blank",children:(0,g.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,g.jsxs)("div",{className:"premium-block-inner",children:[(0,g.jsx)("h4",{children:"FlexSearch"}),(0,g.jsx)("p",{children:"A plugin to efficiently run local fulltext search indexing and queries."})]})})})]})]})}),(0,g.jsx)(w,{open:b,onClose:()=>{v(!1)}})]})})]})}function w({onClose:e,open:r}){return(0,g.jsx)(p._,{onClose:e,open:r,iframeUrl:"https://webforms.pipedrive.com/f/ccHQ5wi8dHxdFgcxEnRfXaXv2uTGnLNwP4tPAGO3hgSFan8xa5j7Kr3LH5OXzWQo2T"})}},9855(e,r,a){a.d(r,{Pu:()=>s});const i={browser:.3,native:.4,performance:.35,server:.2,sourcecode:0};function s(e){const r=e.getLatest()._data.data;return function(e){console.log("-------------------- calculatePrice:"),console.dir(e);let r=0;e.packages.forEach(e=>{r+=i[e]}),console.log("aimInPercent: "+r);let a=200+r/100*6e4;2===e.packages.length?a*=.95:e.packages.length>2&&(a*=.9);const s=Math.pow(e.teamSize,-.4);if(console.log("input.teamSize ("+a+") "+e.teamSize+" - pricePerDeveloper: "+s),a*=s*e.teamSize,e.packages.includes("sourcecode")){a*=1.75;const e=1520;a1200?100*Math.floor(a/100):50*Math.floor(a/50),{totalPrice:a}}({teamSize:r.developers,packages:r.packages})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/0e467ee2.9c93875e.js b/docs/assets/js/0e467ee2.9c93875e.js
deleted file mode 100644
index 1885a5301eb..00000000000
--- a/docs/assets/js/0e467ee2.9c93875e.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4989],{3318(e,n,i){i.r(n),i.d(n,{assets:()=>a,contentTitle:()=>d,default:()=>o,frontMatter:()=>t,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"articles/ideas","title":"ideas for articles","description":"- storing and searching through 1mio emails in a browser database","source":"@site/docs/articles/ideas.md","sourceDirName":"articles","slug":"/articles/ideas","permalink":"/articles/ideas","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{}}');var r=i(4848),l=i(8453);const t={},d="ideas for articles",a={},c=[{value:"Seo keywords:",id:"seo-keywords",level:2},{value:"Seo",id:"seo",level:2},{value:"Non Seo",id:"non-seo",level:2}];function h(e){const n={a:"a",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",ul:"ul",...(0,l.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"ideas-for-articles",children:"ideas for articles"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"storing and searching through 1mio emails in a browser database"}),"\n",(0,r.jsx)(n.li,{children:"Finding the optimal way to shorten vector embeddings"}),"\n",(0,r.jsx)(n.li,{children:"Performance and quality of vector comparison functions (euclideanDistance etc)"}),"\n",(0,r.jsx)(n.li,{children:"performance and quality of vector indexing methods"}),"\n",(0,r.jsx)(n.li,{children:"What is new in IndexedDB 3.0"}),"\n",(0,r.jsx)(n.li,{children:"how progressive syncing beats client-server architecture"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"seo-keywords",children:"Seo keywords:"}),"\n",(0,r.jsxs)(n.p,{children:['X- "optimistic ui"\nX- "local database" (rddt done)\nX- "react-native encryption"\nX- "vue database" (rddt done)\nX- "jquery database"\nX- "vue indexeddb"\nX- "firebase realtime database alternative" (rddt done)\nX- "firestore alternative" (rddt done)\nX- "ionic storage" (rddt done)\nX- "local database"\nX- "offline database"\nX- "zero local first"\nX- "webrtc p2p" - 390 ',(0,r.jsx)(n.a,{href:"http://localhost:3000/replication-webrtc.html",children:"http://localhost:3000/replication-webrtc.html"})]}),"\n",(0,r.jsxs)(n.p,{children:['X- "indexeddb storage limit" - 590 ',(0,r.jsx)(n.a,{href:"https://rxdb.info/articles/indexeddb-max-storage-limit.html",children:"https://rxdb.info/articles/indexeddb-max-storage-limit.html"}),'\nX- "indexeddb size limit" - 260 ',(0,r.jsx)(n.a,{href:"https://rxdb.info/articles/indexeddb-max-storage-limit.html",children:"https://rxdb.info/articles/indexeddb-max-storage-limit.html"}),'\nX- "indexeddb max size" - 590 ',(0,r.jsx)(n.a,{href:"https://rxdb.info/articles/indexeddb-max-storage-limit.html",children:"https://rxdb.info/articles/indexeddb-max-storage-limit.html"}),'\nX- "indexeddb limits" - 170 ',(0,r.jsx)(n.a,{href:"https://rxdb.info/articles/indexeddb-max-storage-limit.html",children:"https://rxdb.info/articles/indexeddb-max-storage-limit.html"})]}),"\n",(0,r.jsx)(n.p,{children:'X- "json based database"\nX- "json vs database"\nX- "reactjs storage"'}),"\n",(0,r.jsx)(n.h2,{id:"seo",children:"Seo"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"supabase alternative"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"store local storage"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react localstorage"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react-native storage"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"supabase offline" - 260'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"store array in localstorage", "localStorage array of objects"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"real time web apps" - 170'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"reactive database" - 210'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"electron sqlite"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"in browser database" - 90'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"offline first app" - 260'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react native sql" - 110'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"sqlite electron"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"localstorage vs indexeddb"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react native nosql database" - 30'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"indexeddb library" - 260'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"indexeddb encryption" - 90'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"client side database" - 140'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"webtransport vs websocket"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"local first development" - 210'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"local storage examples"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"local vector database" - 590'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"mobile app database" - 590'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"web based database"'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"livequery" - 210'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"expo database" - 390'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"database sync" - 8100'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"p2p database" - 170'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"reactive app" - 260'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"offline web app" - 320'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"offline sync" - 320'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react native encrypted storage" - 1000'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"firestore vs firebase" - 1300'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"ionic alternatives" - 480'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react native backend" - 720'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react native alternative" - 1000'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react native sqlite" - 1900'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"flutter vs react native" - 5400'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"react native redux" - 3600'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"redux alternative" - 1300'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"Awesome local first" - 10'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"tauri database" - 170'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"sqlite javascript" - 2900'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"sqlite typescript" - 260'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"sync engine" - 390'}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:'"indexeddb alternative" - 70'}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"non-seo",children:"Non Seo"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:'"Local-First Partial Sync with RxDB"'}),"\n",(0,r.jsx)(n.li,{children:'"why the indexeddb API is almost perfekt"'}),"\n",(0,r.jsx)(n.li,{children:'"how to do auth with RxDB"'}),"\n",(0,r.jsx)(n.li,{children:'"Where to store that JWT token?"'}),"\n"]})]})}function o(e={}){const{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},8453(e,n,i){i.d(n,{R:()=>t,x:()=>d});var s=i(6540);const r={},l=s.createContext(r);function t(e){const n=s.useContext(l);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:t(e.components),s.createElement(l.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/0e945c41.0c53f212.js b/docs/assets/js/0e945c41.0c53f212.js
deleted file mode 100644
index bf66edee9ca..00000000000
--- a/docs/assets/js/0e945c41.0c53f212.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9796],{8453(e,n,r){r.d(n,{R:()=>o,x:()=>a});var s=r(6540);const i={},t=s.createContext(i);function o(e){const n=s.useContext(t);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),s.createElement(t.Provider,{value:n},e.children)}},8908(e,n,r){r.r(n),r.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"replication-server","title":"RxDB Server Replication","description":"The Server Replication Plugin connects to the replication endpoint of an RxDB Server Replication Endpoint and replicates data between the client and the server.","source":"@site/docs/replication-server.md","sourceDirName":".","slug":"/replication-server.html","permalink":"/replication-server.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB Server Replication","slug":"replication-server.html"},"sidebar":"tutorialSidebar","previous":{"title":"HTTP Replication","permalink":"/replication-http.html"},"next":{"title":"GraphQL Replication","permalink":"/replication-graphql.html"}}');var i=r(4848),t=r(8453);const o={title:"RxDB Server Replication",slug:"replication-server.html"},a="RxDB Server Replication",l={},c=[{value:"Usage",id:"usage",level:2},{value:"outdatedClient$",id:"outdatedclient",level:2},{value:"unauthorized$",id:"unauthorized",level:2},{value:"forbidden$",id:"forbidden",level:2},{value:"Custom EventSource implementation",id:"custom-eventsource-implementation",level:2}];function d(e){const n={a:"a",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",...(0,t.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"rxdb-server-replication",children:"RxDB Server Replication"})}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.em,{children:"Server Replication Plugin"})," connects to the replication endpoint of an ",(0,i.jsx)(n.a,{href:"/rx-server.html#replication-endpoint",children:"RxDB Server Replication Endpoint"})," and replicates data between the client and the server."]}),"\n",(0,i.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(n.p,{children:["The replication server plugin is imported from the ",(0,i.jsx)(n.code,{children:"rxdb-server"})," npm package. Then you start the replication with a given collection and endpoint url by calling ",(0,i.jsx)(n.code,{children:"replicateServer()"}),"."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateServer } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-server/plugins/replication-server'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateServer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" usersCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-server-replication'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://localhost:80/users/0'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // endpoint url with the servers collection schema version at the end"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Authorization"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bearer S0VLU0UhI...'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"outdatedclient",children:"outdatedClient$"}),"\n",(0,i.jsxs)(n.p,{children:["When you update your schema at the server and run a migration, you end up with a different replication url that has a new schema version number at the end.\nYour clients might still be running an old version of your application that will no longer be compatible with the endpoint. Therefore when the client tries to call a server endpoint with an outdated schema version, the ",(0,i.jsx)(n.code,{children:"outdatedClient$"})," observable emits to tell your client that the application must be updated. With that event you can tell the client to update the application.\nOn browser application you might want to just reload the page on that event:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"outdatedClient$"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" location"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".reload"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"unauthorized",children:"unauthorized$"}),"\n",(0,i.jsxs)(n.p,{children:["When you clients auth data is not valid (or no longer valid), the server will no longer accept any requests from you client and inform the client that the auth headers must be updated.\nThe ",(0,i.jsx)(n.code,{children:"unauthorized$"})," observable will emit and expects you to update the headers accordingly so that following requests will be accepted again."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"unauthorized$"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".setHeaders"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Authorization"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bearer S0VLU0UhI...'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"forbidden",children:"forbidden$"}),"\n",(0,i.jsxs)(n.p,{children:["When you client behaves wrong in any case, like update non-allowed values or changing documents that it is not allowed to,\nthe server will drop the connection and the replication state will emit on the ",(0,i.jsx)(n.code,{children:"forbidden$"})," observable.\nIt will also automatically stop the replication so that your client does not accidentally DOS attack the server."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"forbidden$"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Client is behaving wrong'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"custom-eventsource-implementation",children:"Custom EventSource implementation"}),"\n",(0,i.jsxs)(n.p,{children:["For the server send events, the ",(0,i.jsx)(n.a,{href:"https://github.com/EventSource/eventsource",children:"eventsource"})," npm package is used instead of the native ",(0,i.jsx)(n.code,{children:"EventSource"})," API. We need this because the native browser API does not support sending headers with the request which is required by the server to parse the auth data."]}),"\n",(0,i.jsx)(n.p,{children:"If the eventsource package does not work for you, you can set an own implementation when creating the replication."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateServer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" eventSource"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" MyEventSourceConstructor"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]})}function h(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/0f6e10f0.51b093f2.js b/docs/assets/js/0f6e10f0.51b093f2.js
deleted file mode 100644
index ec885888dcb..00000000000
--- a/docs/assets/js/0f6e10f0.51b093f2.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1475],{7706(e,i,s){s.r(i),s.d(i,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>a,toc:()=>d});const a=JSON.parse('{"id":"articles/progressive-web-app-database","title":"RxDB as a Database for Progressive Web Apps (PWA)","description":"Discover how RxDB supercharges Progressive Web Apps with real-time sync, offline-first capabilities, and lightning-fast data handling.","source":"@site/docs/articles/progressive-web-app-database.md","sourceDirName":"articles","slug":"/articles/progressive-web-app-database.html","permalink":"/articles/progressive-web-app-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB as a Database for Progressive Web Apps (PWA)","slug":"progressive-web-app-database.html","description":"Discover how RxDB supercharges Progressive Web Apps with real-time sync, offline-first capabilities, and lightning-fast data handling."},"sidebar":"tutorialSidebar","previous":{"title":"Real-Time & Offline - RxDB for Mobile Apps","permalink":"/articles/mobile-database.html"},"next":{"title":"RxDB as a Database for React Applications","permalink":"/articles/react-database.html"}}');var n=s(4848),r=s(8453);const t={title:"RxDB as a Database for Progressive Web Apps (PWA)",slug:"progressive-web-app-database.html",description:"Discover how RxDB supercharges Progressive Web Apps with real-time sync, offline-first capabilities, and lightning-fast data handling."},o="RxDB as a Database for Progressive Web Apps (PWA)",l={},d=[{value:"What is a Progressive Web App",id:"what-is-a-progressive-web-app",level:2},{value:"Introducing RxDB as a Client-Side Database for PWAs",id:"introducing-rxdb-as-a-client-side-database-for-pwas",level:2},{value:"Getting Started with RxDB",id:"getting-started-with-rxdb",level:3},{value:"Local-First Approach",id:"local-first-approach",level:4},{value:"Observable Queries",id:"observable-queries",level:4},{value:"Multi-Tab Support",id:"multi-tab-support",level:4},{value:"Using RxDB in a Progressive Web App",id:"using-rxdb-in-a-progressive-web-app",level:3},{value:"Exploring Different RxStorage Layers",id:"exploring-different-rxstorage-layers",level:2},{value:"Advanced RxDB Features and Techniques",id:"advanced-rxdb-features-and-techniques",level:2},{value:"Encryption of Local Data",id:"encryption-of-local-data",level:3},{value:"Indexing and Performance Optimization",id:"indexing-and-performance-optimization",level:3},{value:"JSON Key Compression",id:"json-key-compression",level:3},{value:"Change Streams and Event Handling",id:"change-streams-and-event-handling",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Follow Up",id:"follow-up",level:2}];function c(e){const i={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(i.header,{children:(0,n.jsx)(i.h1,{id:"rxdb-as-a-database-for-progressive-web-apps-pwa",children:"RxDB as a Database for Progressive Web Apps (PWA)"})}),"\n",(0,n.jsx)(i.p,{children:"Progressive Web Apps (PWAs) have revolutionized the digital landscape, offering users an immersive blend of web and native app experiences. At the heart of every successful PWA lies effective data management, and this is where RxDB comes into play. In this article, we'll explore the dynamic synergy between RxDB, a robust client-side database, and Progressive Web Apps, uncovering how RxDB enhances data handling, synchronization, and overall performance, propelling PWAs into a new era of excellence."}),"\n",(0,n.jsx)(i.h2,{id:"what-is-a-progressive-web-app",children:"What is a Progressive Web App"}),"\n",(0,n.jsx)(i.p,{children:"Progressive Web Apps are the future of web development, seamlessly combining the best of both web and mobile app worlds. They can be easily installed on the user's home screen, function offline, and load at lightning speed. Unlike hybrid apps, PWAs offer a consistent user experience across platforms, making them a versatile choice for modern applications."}),"\n",(0,n.jsx)(i.p,{children:"PWAs bring a plethora of advantages to the table. They eliminate the hassle of app store installations and updates, reduce dependency on network connectivity, and prioritize fast loading times. By harnessing the power of service workers and intelligent caching mechanisms, PWAs ensure users can access content even in offline mode. Furthermore, PWAs are device-agnostic, seamlessly adapting to various devices, from desktops to smartphones."}),"\n",(0,n.jsx)(i.h2,{id:"introducing-rxdb-as-a-client-side-database-for-pwas",children:"Introducing RxDB as a Client-Side Database for PWAs"}),"\n",(0,n.jsx)(i.p,{children:"At the heart of PWAs lies efficient data management, and RxDB steps in as a reliable ally. As a client-side NoSQL database, RxDB seamlessly integrates into web applications, offering real-time data synchronization and manipulation capabilities. This article sheds light on the transformative potential of RxDB as it collaborates harmoniously with PWAs, enabling local-first strategies and elevating user interactions to a whole new level."}),"\n",(0,n.jsx)("center",{children:(0,n.jsx)("a",{href:"https://rxdb.info/",children:(0,n.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"Progressive Web App Database",width:"228"})})}),"\n",(0,n.jsx)(i.h3,{id:"getting-started-with-rxdb",children:"Getting Started with RxDB"}),"\n",(0,n.jsx)(i.p,{children:"RxDB emerges as a reactive, schema-based NoSQL database crafted explicitly for client-side applications. Its real-time data synchronization and responsiveness align seamlessly with the dynamic demands of modern PWAs."}),"\n",(0,n.jsx)(i.h4,{id:"local-first-approach",children:"Local-First Approach"}),"\n",(0,n.jsx)(i.p,{children:"The cornerstone of RxDB's philosophy is the local-first approach, empowering PWAs to prioritize data storage and manipulation on the client side. This paradigm ensures that PWAs remain functional even when offline, allowing users to access and interact with data seamlessly. RxDB bridges any gaps in data synchronization once network connectivity is restored."}),"\n",(0,n.jsx)(i.h4,{id:"observable-queries",children:"Observable Queries"}),"\n",(0,n.jsxs)(i.p,{children:["Observable queries (aka ",(0,n.jsx)(i.strong,{children:"Live-Queries"}),") serve as the engine of RxDB's dynamic capabilities. By leveraging these queries, PWAs can monitor and respond to data changes in real time. The result is an engaging user interface with instantaneous updates that captivate users and keep them engaged."]}),"\n",(0,n.jsx)(i.figure,{"data-rehype-pretty-code-figure":"",children:(0,n.jsx)(i.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,n.jsxs)(i.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,n.jsxs)(i.span,{"data-line":"",children:[(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,n.jsxs)(i.span,{"data-line":"",children:[(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,n.jsxs)(i.span,{"data-line":"",children:[(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:" healthpoints"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,n.jsxs)(i.span,{"data-line":"",children:[(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"})]}),"\n",(0,n.jsx)(i.span,{"data-line":"",children:(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,n.jsx)(i.span,{"data-line":"",children:(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,n.jsx)(i.span,{"data-line":"",children:(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,n.jsxs)(i.span,{"data-line":"",children:[(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:".$ "}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-comment)"},children:"// the $ returns an observable that emits each time the result set of the query changes"})]}),"\n",(0,n.jsxs)(i.span,{"data-line":"",children:[(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:"(aliveHeroes "}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,n.jsx)(i.span,{style:{color:"var(--shiki-foreground)"},children:"(aliveHeroes));"})]})]})})}),"\n",(0,n.jsx)(i.h4,{id:"multi-tab-support",children:"Multi-Tab Support"}),"\n",(0,n.jsx)(i.p,{children:"RxDB extends its prowess to multi-tab scenarios, guaranteeing data consistency across different tabs or windows of the same PWA. This feature promotes a seamless transition between various sections of the application, while minimizing data conflicts."}),"\n",(0,n.jsx)("p",{align:"center",children:(0,n.jsx)("img",{src:"../files/multiwindow.gif",alt:"multi tab support",width:"450"})}),"\n",(0,n.jsx)(i.h3,{id:"using-rxdb-in-a-progressive-web-app",children:"Using RxDB in a Progressive Web App"}),"\n",(0,n.jsx)(i.p,{children:"Integrating RxDB into a Progressive Web App, driven by technologies like React, is a straightforward process. By configuring RxDB and installing the necessary packages, developers establish a solid foundation for robust data management within their PWA."}),"\n",(0,n.jsx)(i.h2,{id:"exploring-different-rxstorage-layers",children:"Exploring Different RxStorage Layers"}),"\n",(0,n.jsx)(i.p,{children:"RxDB caters to diverse needs through its various RxStorage layers:"}),"\n",(0,n.jsxs)(i.ul,{children:["\n",(0,n.jsxs)(i.li,{children:[(0,n.jsx)(i.a,{href:"/rx-storage-localstorage.html",children:"localstorage RxStorage"}),": Leveraging the capabilities of the browsers localstorage API for storage."]}),"\n",(0,n.jsxs)(i.li,{children:[(0,n.jsx)(i.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),": Tapping into the browser's IndexedDB for efficient data storage."]}),"\n",(0,n.jsxs)(i.li,{children:[(0,n.jsx)(i.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"}),": Interfacing with the Offline-First Persistence System for seamless persistence."]}),"\n",(0,n.jsxs)(i.li,{children:[(0,n.jsx)(i.a,{href:"/rx-storage-memory.html",children:"Memory RxStorage"}),": Storing data in memory, ideal for temporary data requirements.\nThis flexibility empowers developers to optimize data storage based on the unique needs of their PWA."]}),"\n"]}),"\n",(0,n.jsx)(i.p,{children:"Synchronizing Data with RxDB between PWA Clients and Servers\nTo facilitate seamless data synchronization between PWA clients and servers, RxDB offers a range of replication options:"}),"\n",(0,n.jsxs)(i.ul,{children:["\n",(0,n.jsxs)(i.li,{children:["\n",(0,n.jsxs)(i.p,{children:[(0,n.jsx)(i.a,{href:"/replication.html",children:"RxDB Replication Algorithm"}),": RxDB introduces its own replication algorithm, enabling efficient and reliable data synchronization between clients and servers."]}),"\n"]}),"\n",(0,n.jsxs)(i.li,{children:["\n",(0,n.jsxs)(i.p,{children:[(0,n.jsx)(i.a,{href:"/replication-couchdb.html",children:"CouchDB Replication"}),": Leveraging its roots in CouchDB, RxDB facilitates smooth data replication between clients and CouchDB servers, ensuring data consistency and synchronization across devices."]}),"\n"]}),"\n",(0,n.jsxs)(i.li,{children:["\n",(0,n.jsxs)(i.p,{children:[(0,n.jsx)(i.a,{href:"/replication-firestore.html",children:"Firestore Replication"}),": RxDB synchronizes data with Google Firestore, a real-time cloud-hosted NoSQL database. This integration guarantees up-to-date data across different instances of the PWA."]}),"\n"]}),"\n",(0,n.jsxs)(i.li,{children:["\n",(0,n.jsxs)(i.p,{children:[(0,n.jsx)(i.a,{href:"/replication-webrtc.html",children:"Peer-to-Peer (P2P) via WebRTC"})," Replication: RxDB supports P2P replication, facilitating direct data synchronization between clients without intermediaries. This decentralized approach is invaluable in scenarios where server infrastructure is limited."]}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(i.h2,{id:"advanced-rxdb-features-and-techniques",children:"Advanced RxDB Features and Techniques"}),"\n",(0,n.jsx)(i.h3,{id:"encryption-of-local-data",children:"Encryption of Local Data"}),"\n",(0,n.jsx)(i.p,{children:"RxDB empowers PWAs with the ability to encrypt local data, enhancing data security and safeguarding sensitive information. This feature is indispensable for applications handling user credentials, financial transactions, and other confidential data."}),"\n",(0,n.jsx)(i.h3,{id:"indexing-and-performance-optimization",children:"Indexing and Performance Optimization"}),"\n",(0,n.jsx)(i.p,{children:"Performance optimization is a top priority for PWAs. RxDB addresses this concern by offering indexing options that expedite data retrieval, resulting in a snappier user interface and heightened responsiveness."}),"\n",(0,n.jsx)(i.h3,{id:"json-key-compression",children:"JSON Key Compression"}),"\n",(0,n.jsx)(i.p,{children:"RxDB introduces JSON key compression, a feature that reduces storage requirements. This optimization is particularly beneficial for PWAs dealing with substantial data volumes, enhancing overall efficiency and resource utilization."}),"\n",(0,n.jsx)(i.h3,{id:"change-streams-and-event-handling",children:"Change Streams and Event Handling"}),"\n",(0,n.jsx)(i.p,{children:"RxDB introduces change streams, enabling PWAs to react to data changes in real time. This capability empowers dynamic updates to the user interface, promoting interactivity and engagement."}),"\n",(0,n.jsx)(i.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,n.jsx)(i.p,{children:"In the ever-evolving landscape of web application development, Progressive Web Apps continue to redefine user experiences. RxDB emerges as a pivotal player, seamlessly integrating with PWAs and enhancing their capabilities. With features like the local-first approach, observable queries, replication mechanisms, and advanced encryption, RxDB empowers developers to create responsive, offline-capable, and data-driven PWAs. As the demand for sophisticated PWAs continues to surge, RxDB remains an indispensable tool for developers aiming to push the boundaries of innovation and redefine the standards of user engagement. By embracing RxDB, developers ensure their PWAs remain at the forefront of the digital revolution, offering seamless and immersive experiences to users around the world."}),"\n",(0,n.jsx)(i.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,n.jsx)(i.p,{children:"To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:"}),"\n",(0,n.jsxs)(i.ul,{children:["\n",(0,n.jsxs)(i.li,{children:[(0,n.jsx)(i.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB GitHub Repository"}),": Visit the official GitHub repository of RxDB to access the source code, documentation, and community support."]}),"\n",(0,n.jsxs)(i.li,{children:[(0,n.jsx)(i.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),": Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects."]}),"\n",(0,n.jsx)(i.li,{children:(0,n.jsx)(i.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/angular",children:"RxDB Progressive Web App in Angular Example"})}),"\n"]})]})}function h(e={}){const{wrapper:i}={...(0,r.R)(),...e.components};return i?(0,n.jsx)(i,{...e,children:(0,n.jsx)(c,{...e})}):c(e)}},8453(e,i,s){s.d(i,{R:()=>t,x:()=>o});var a=s(6540);const n={},r=a.createContext(n);function t(e){const i=a.useContext(r);return a.useMemo(function(){return"function"==typeof e?e(i):{...i,...e}},[i,e])}function o(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:t(e.components),a.createElement(r.Provider,{value:i},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/118cde4c.d7e82ca5.js b/docs/assets/js/118cde4c.d7e82ca5.js
deleted file mode 100644
index 1d613b431fc..00000000000
--- a/docs/assets/js/118cde4c.d7e82ca5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5272],{2318(e,n,s){s.r(n),s.d(n,{assets:()=>t,contentTitle:()=>a,default:()=>d,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"replication-couchdb","title":"RxDB\'s CouchDB Replication Plugin","description":"Replicate your RxDB collections with CouchDB the fast way. Enjoy faster sync, easier conflict handling, and flexible storage using this modern plugin.","source":"@site/docs/replication-couchdb.md","sourceDirName":".","slug":"/replication-couchdb.html","permalink":"/replication-couchdb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB\'s CouchDB Replication Plugin","slug":"replication-couchdb.html","description":"Replicate your RxDB collections with CouchDB the fast way. Enjoy faster sync, easier conflict handling, and flexible storage using this modern plugin."},"sidebar":"tutorialSidebar","previous":{"title":"WebSocket Replication","permalink":"/replication-websocket.html"},"next":{"title":"WebRTC P2P Replication","permalink":"/replication-webrtc.html"}}');var i=s(4848),o=s(8453);const l={title:"RxDB's CouchDB Replication Plugin",slug:"replication-couchdb.html",description:"Replicate your RxDB collections with CouchDB the fast way. Enjoy faster sync, easier conflict handling, and flexible storage using this modern plugin."},a="Replication with CouchDB",t={},c=[{value:"Pros",id:"pros",level:2},{value:"Cons",id:"cons",level:2},{value:"Usage",id:"usage",level:2},{value:"Conflict handling",id:"conflict-handling",level:2},{value:"Auth example",id:"auth-example",level:2},{value:"Limitations",id:"limitations",level:2},{value:"Known problems",id:"known-problems",level:2},{value:"Database missing",id:"database-missing",level:3},{value:"React Native",id:"react-native",level:2}];function h(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"replication-with-couchdb",children:"Replication with CouchDB"})}),"\n",(0,i.jsx)(n.p,{children:"A plugin to replicate between a RxCollection and a CouchDB server."}),"\n",(0,i.jsxs)(n.p,{children:["This plugins uses the RxDB ",(0,i.jsx)(n.a,{href:"/replication.html",children:"Sync Engine"})," to replicate with a CouchDB endpoint. This plugin ",(0,i.jsx)(n.strong,{children:"does NOT"})," use the official ",(0,i.jsx)(n.a,{href:"https://docs.couchdb.org/en/stable/replication/protocol.html",children:"CouchDB replication protocol"})," because the CouchDB protocol was optimized for server-to-server replication and is not suitable for fast client side applications, mostly because it has to run many HTTP-requests (at least one per document) and also it has to store the whole revision tree of the documents at the client. This makes initial replication and querying very slow."]}),"\n",(0,i.jsx)(n.p,{children:"Because the way how RxDB handles revisions and documents is very similar to CouchDB, using the RxDB replication with a CouchDB endpoint is pretty straightforward."}),"\n",(0,i.jsx)(n.h2,{id:"pros",children:"Pros"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Faster initial replication."}),"\n",(0,i.jsxs)(n.li,{children:["Works with any ",(0,i.jsx)(n.a,{href:"/rx-storage.html",children:"RxStorage"}),", not just PouchDB."]}),"\n",(0,i.jsx)(n.li,{children:"Easier conflict handling because conflicts are handled during replication and not afterwards."}),"\n",(0,i.jsx)(n.li,{children:"Does not have to store all document revisions on the client, only stores the newest version."}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"cons",children:"Cons"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Does not support the replication of ",(0,i.jsx)(n.a,{href:"/rx-attachment.html",children:"attachments"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Like all CouchDB replication plugins, this one is also limited to replicating 6 collections in parallel. ",(0,i.jsx)(n.a,{href:"/replication-couchdb.html#limitations",children:"Read this for workarounds"})]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(n.p,{children:["Start the replication via ",(0,i.jsx)(n.code,{children:"replicateCouchDB()"}),"."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateCouchDB } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-couchdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateCouchDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-couchdb-replication'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // url to the CouchDB endpoint (required)"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/db/humans'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * true for live replication,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * false for a one-time replication."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=true]"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * A custom fetch() method can be provided"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * to add authentication or credentials."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Can be swapped out dynamically"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * by running 'replicationState.fetch = newFetchMethod;'."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" fetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCustomFetchMethod"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Amount of documents to be fetched in one HTTP request"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Custom modifier to mutate pulled documents"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * before storing them in RxDB."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" modifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docData "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" "})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Heartbeat time in milliseconds"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * for the long polling of the changestream."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" https://docs.couchdb.org/en/3.2.2-docs/api/database/changes.html"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional, default=60000)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heartbeat"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60000"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * How many local changes to process at once."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Custom modifier to mutate documents"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * before sending them to the CouchDB endpoint."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" modifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docData "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["When you call ",(0,i.jsx)(n.code,{children:"replicateCouchDB()"})," it returns a ",(0,i.jsx)(n.code,{children:"RxCouchDBReplicationState"})," which can be used to subscribe to events, for debugging or other functions. It extends the ",(0,i.jsx)(n.a,{href:"/replication.html",children:"RxReplicationState"})," so any other method that can be used there can also be used on the CouchDB replication state."]}),"\n",(0,i.jsx)(n.h2,{id:"conflict-handling",children:"Conflict handling"}),"\n",(0,i.jsxs)(n.p,{children:["When conflicts appear during replication, the ",(0,i.jsx)(n.code,{children:"conflictHandler"})," of the ",(0,i.jsx)(n.code,{children:"RxCollection"})," is used, equal to the other replication plugins. Read more about conflict handling ",(0,i.jsx)(n.a,{href:"/replication.html#conflict-handling",children:"here"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"auth-example",children:"Auth example"}),"\n",(0,i.jsxs)(n.p,{children:["Lets say for authentication you need to add a ",(0,i.jsx)(n.a,{href:"https://swagger.io/docs/specification/authentication/bearer-authentication/",children:"bearer token"})," as HTTP header to each request. You can achieve that by crafting a custom ",(0,i.jsx)(n.code,{children:"fetch()"})," method that add the header field."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" myCustomFetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" options) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // flat clone the given options to not mutate the input"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" optionsWithAuth"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Object"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".assign"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" options);"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // ensure the headers property exists"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"optionsWithAuth"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".headers) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" optionsWithAuth"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".headers "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {};"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // add bearer token to headers"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" optionsWithAuth"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".headers["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Authorization'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"] "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Basic S0VLU0UhIExFQ0...'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // call the original fetch function with our custom options."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" optionsWithAuth"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateCouchDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-couchdb-replication'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/db/humans'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Add the custom fetch function here."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" fetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCustomFetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["Also when your bearer token changes over time, you can set a new custom ",(0,i.jsx)(n.code,{children:"fetch"})," method while the replication is running:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".fetch "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" newCustomFetchMethod;"})]})})})}),"\n",(0,i.jsxs)(n.p,{children:["Also there is a helper method ",(0,i.jsx)(n.code,{children:"getFetchWithCouchDBAuthorization()"})," to create a fetch handler with authorization:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { "})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicateCouchDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getFetchWithCouchDBAuthorization"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-couchdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateCouchDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-couchdb-replication'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/db/humans'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Add the custom fetch function here."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" fetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getFetchWithCouchDBAuthorization"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'myUsername'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myPassword'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"limitations",children:"Limitations"}),"\n",(0,i.jsxs)(n.p,{children:["Since CouchDB only allows synchronization through HTTP1.1 long polling requests there is a limitation of 6 active synchronization connections before the browser prevents sending any further request. This limitation is at the level of browser per tab per domain (some browser, especially older ones, might have a different limit, ",(0,i.jsx)(n.a,{href:"https://docs.pushtechnology.com/cloud/latest/manual/html/designguide/solution/support/connection_limitations.html",children:"see here"}),")."]}),"\n",(0,i.jsxs)(n.p,{children:["Since this limitation is at the ",(0,i.jsx)(n.strong,{children:"browser"})," level there are several solutions:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:'Use only a single database for all entities and set a "type" field for each of the documents'}),"\n",(0,i.jsx)(n.li,{children:"Create multiple subdomains for CouchDB and use a max of 6 active synchronizations (or less) for each"}),"\n",(0,i.jsx)(n.li,{children:"Use a proxy (ex: HAProxy) between the browser and CouchDB and configure it to use HTTP2.0, since HTTP2.0"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"If you use nginx in front of your CouchDB, you can use these settings to enable http2-proxying to prevent the connection limit problem:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:'server {\n http2 on;\n location /db {\n rewrite /db/(.*) /$1 break;\n proxy_pass http://172.0.0.1:5984;\n proxy_redirect off;\n proxy_buffering off;\n proxy_set_header Host $host;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded\n proxy_set_header Connection "keep_alive"\n }\n}\n'})}),"\n",(0,i.jsx)(n.h2,{id:"known-problems",children:"Known problems"}),"\n",(0,i.jsx)(n.h3,{id:"database-missing",children:"Database missing"}),"\n",(0,i.jsxs)(n.p,{children:["In contrast to PouchDB, this plugin ",(0,i.jsx)(n.strong,{children:"does NOT"})," automatically create missing CouchDB databases.\nIf your CouchDB server does not have a database yet, you have to create it by yourself by running a ",(0,i.jsx)(n.code,{children:"PUT"})," request to the database ",(0,i.jsx)(n.code,{children:"name"})," url:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// create a 'humans' CouchDB database on the server"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" remoteDatabaseName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'humans'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/db/'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" remoteDatabaseName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" method"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'PUT'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"react-native",children:"React Native"}),"\n",(0,i.jsxs)(n.p,{children:["React Native does not have a global ",(0,i.jsx)(n.code,{children:"fetch"})," method. You have to import fetch method with the ",(0,i.jsx)(n.a,{href:"https://www.npmjs.com/package/cross-fetch",children:"cross-fetch"})," package:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" crossFetch "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'cross-fetch'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateCouchDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-couchdb-replication'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/db/humans'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" fetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" crossFetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})})]})}function d(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,n,s){s.d(n,{R:()=>l,x:()=>a});var r=s(6540);const i={},o=r.createContext(i);function l(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:l(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/11d75f9a.8210c243.js b/docs/assets/js/11d75f9a.8210c243.js
deleted file mode 100644
index d6bbf20baf0..00000000000
--- a/docs/assets/js/11d75f9a.8210c243.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8897],{8282(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/react-native-encryption","title":"React Native Encryption and Encrypted Database/Storage","description":"Secure your React Native app with RxDB encryption. Learn why it matters, how to implement encrypted databases, and best practices to protect user data.","source":"@site/docs/articles/react-native-encryption.md","sourceDirName":"articles","slug":"/articles/react-native-encryption.html","permalink":"/articles/react-native-encryption.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"React Native Encryption and Encrypted Database/Storage","slug":"react-native-encryption.html","description":"Secure your React Native app with RxDB encryption. Learn why it matters, how to implement encrypted databases, and best practices to protect user data."},"sidebar":"tutorialSidebar","previous":{"title":"What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications","permalink":"/articles/local-database.html"},"next":{"title":"RxDB as a Database in a Vue.js Application","permalink":"/articles/vue-database.html"}}');var i=s(4848),a=s(8453);const o={title:"React Native Encryption and Encrypted Database/Storage",slug:"react-native-encryption.html",description:"Secure your React Native app with RxDB encryption. Learn why it matters, how to implement encrypted databases, and best practices to protect user data."},t="React Native Encryption and Encrypted Database/Storage",l={},c=[{value:"\ud83d\udd12 Why Encryption Matters",id:"-why-encryption-matters",level:2},{value:"React Native Encryption Overview",id:"react-native-encryption-overview",level:2},{value:"Setting Up Encryption in RxDB for React Native",id:"setting-up-encryption-in-rxdb-for-react-native",level:2},{value:"1. Install RxDB and Required Plugins",id:"1-install-rxdb-and-required-plugins",level:3},{value:"2. Set Up Your RxDB Database with Encryption",id:"2-set-up-your-rxdb-database-with-encryption",level:3},{value:"3. Inserting and Querying Encrypted Data",id:"3-inserting-and-querying-encrypted-data",level:3},{value:"Best Practices for React Native Encryption",id:"best-practices-for-react-native-encryption",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"react-native-encryption-and-encrypted-databasestorage",children:"React Native Encryption and Encrypted Database/Storage"})}),"\n",(0,i.jsx)(n.p,{children:"Data security is a critical concern in modern mobile applications. As React Native continues to grow in popularity for building cross-platform apps, ensuring that your data is protected is paramount. RxDB, a real-time database for JavaScript applications, offers powerful encryption features that can help you secure your React Native app's data."}),"\n",(0,i.jsxs)(n.p,{children:["This article explains why encryption is important, how to set it up with RxDB in ",(0,i.jsx)(n.a,{href:"/react-native-database.html",children:"React Native"}),", and best practices to keep your app secure."]}),"\n",(0,i.jsx)(n.h2,{id:"-why-encryption-matters",children:"\ud83d\udd12 Why Encryption Matters"}),"\n",(0,i.jsxs)(n.p,{children:["Encryption ensures that, even if an unauthorized party obtains physical access to your device or intercepts data, they cannot read the information without the encryption key. Sensitive user data such as credentials, personal information, or financial details should always be encrypted. Proper encryption practices reduce the risk of data breaches and help your application remain compliant with regulations like ",(0,i.jsx)(n.a,{href:"https://gdpr.eu/",children:"GDPR"})," or ",(0,i.jsx)(n.a,{href:"https://www.hhs.gov/hipaa/index.html",children:"HIPAA"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"react-native-encryption-overview",children:"React Native Encryption Overview"}),"\n",(0,i.jsx)(n.p,{children:"React Native supports multiple ways to secure local data:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Encrypted Databases"}),"\nUse databases with built-in encryption capabilities, such as SQLite with encryption layers or RxDB with its ",(0,i.jsx)(n.a,{href:"/encryption.html",children:"encryption plugin"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Secure Storage Libraries"}),"\nFor key-value data (like tokens or secrets), you can use libraries like ",(0,i.jsx)(n.a,{href:"https://github.com/oblador/react-native-keychain",children:"react-native-keychain"})," or ",(0,i.jsx)(n.a,{href:"https://github.com/emeraldsanto/react-native-encrypted-storage",children:"react-native-encrypted-storage"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Custom Encryption"}),"\nIf you need more fine-grained control, you can integrate libraries like ",(0,i.jsx)(n.a,{href:"https://github.com/brix/crypto-js",children:(0,i.jsx)(n.code,{children:"crypto-js"})})," or the ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API",children:"Web Crypto API"})," to encrypt data before storing it in a database or file."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"/files/logo/rxdb_javascript_database.svg",alt:"RxDB",width:"250"})})}),"\n",(0,i.jsx)(n.h2,{id:"setting-up-encryption-in-rxdb-for-react-native",children:"Setting Up Encryption in RxDB for React Native"}),"\n",(0,i.jsx)(n.h3,{id:"1-install-rxdb-and-required-plugins",children:"1. Install RxDB and Required Plugins"}),"\n",(0,i.jsx)(n.p,{children:"Install RxDB and the encryption plugin(s) you need. For the CryptoJS plugin:"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" crypto-js"})]})]})})}),"\n",(0,i.jsx)(n.h3,{id:"2-set-up-your-rxdb-database-with-encryption",children:"2. Set Up Your RxDB Database with Encryption"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB offers two ",(0,i.jsx)(n.a,{href:"/encryption.html",children:"encryption plugins"}),":"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"CryptoJS Plugin"}),": A free and straightforward solution for most basic use cases."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Web Crypto Plugin"}),": A ",(0,i.jsx)(n.a,{href:"/premium",children:"premium plugin"})," that utilizes the native Web Crypto API for better performance and security."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Below is an example showing how to set up RxDB using the CryptoJS plugin. This example uses the ",(0,i.jsx)(n.a,{href:"/rx-storage-memory.html",children:"in-memory storage"})," for testing purposes. In a real production scenario, you would use a persistent storage adapter, mostly the ",(0,i.jsx)(n.a,{href:"/rx-storage-sqlite.html",children:"SQLite-based storage"}),"."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedKeyEncryptionCryptoJsStorage } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/encryption-crypto-js'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/*"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * For testing, we use the in-memory storage of RxDB."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * In production you would use the persistent SQLite based storage instead."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageMemory } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-memory'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" initEncryptedDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Wrap the normal storage with the encryption plugin"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" encryptedMemoryStorage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyEncryptionCryptoJsStorage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMemory"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Create an encrypted database"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myEncryptedDatabase'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" encryptedMemoryStorage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'sudoLetMeIn'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Make sure not to hardcode in production"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Define a schema and create a collection"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" secureData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'secure data schema'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" normalField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" secretField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'normalField'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'secretField'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(n.h3,{id:"3-inserting-and-querying-encrypted-data",children:"3. Inserting and Querying Encrypted Data"}),"\n",(0,i.jsx)(n.p,{children:"Once you've set up the database with encryption, data in fields specified by your schema will be encrypted automatically before it is stored, and decrypted when queried."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" initEncryptedDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Insert encrypted data"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"secureData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mySecretId'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" normalField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" secretField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'This is top secret data'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Query encrypted data by its primary key or non-encrypted fields"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" fetchedDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"secureData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" normalField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"fetchedDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".secretField); "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'This is top secret data'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Update data"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" fetchedDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".patch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" secretField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Updated secret data'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})();"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Note"}),": You can only query directly by non-encrypted fields or primary keys. Encrypted fields cannot be used in queries because they are stored as ciphertext in the database. A common approach is to have a small subset of fields that need to be queried unencrypted while storing any sensitive data in encrypted fields."]}),"\n",(0,i.jsx)(n.h2,{id:"best-practices-for-react-native-encryption",children:"Best Practices for React Native Encryption"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Secure Password Handling"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Avoid hardcoding passwords or encryption keys."}),"\n",(0,i.jsx)(n.li,{children:"Use secure storage solutions like React Native Keychain or react-native-encrypted-storage to fetch the database password at runtime:"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Example: using react-native-keychain to securely retrieve a stored password"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" *"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" as"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Keychain "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'react-native-keychain'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getDatabasePassword"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" credentials"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Keychain"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".getGenericPassword"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (credentials) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" credentials"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".password;"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" throw"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" Error"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'No password stored in Keychain'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Encrypt Attachments"}),":\nIf you need to store files (images, text files, etc.), consider encrypting attachments. RxDB supports attachments that can be encrypted automatically, ensuring your files are protected:"]}),"\n"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createBlob } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"secureData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" normalField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".putAttachment"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'encryptedFile.txt'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" data"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createBlob"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Sensitive content'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/plain'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/plain'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Optimize Performance"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"If performance is critical, consider using the premium Web Crypto plugin, which leverages native APIs for faster encryption and decryption."}),"\n",(0,i.jsx)(n.li,{children:"If big chunks of data are encrypted, store them in attachments instead of document fields. Attachments will only be decrypted on explicit fetches, not during queries."}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Use DevMode in Development"}),": RxDB's ",(0,i.jsx)(n.a,{href:"/dev-mode.html",children:"DevMode Plugin"})," can help validate your schema and encryption setup during development. Disable it in production for performance reasons."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Secure Communication"}),":"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Use HTTPS to secure network communication between the app and any backend services."}),"\n",(0,i.jsxs)(n.li,{children:["If you're synchronizing data to a server, ensure the data is also encrypted in transit. RxDB's ",(0,i.jsx)(n.a,{href:"/replication.html",children:"replication plugins"})," can work with secure endpoints to keep data consistent."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"SSL Pinning"}),": Consider SSL Pinning if you want to prevent man-in-the-middle attacks. SSL Pinning ensures the device trusts only the pinned certificate, preventing attackers from swapping out valid certificates with their own."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Learn how to use RxDB with the ",(0,i.jsx)(n.a,{href:"/quickstart.html",children:"RxDB Quickstart"})," for a guided introduction."]}),"\n",(0,i.jsxs)(n.li,{children:["A good way to learn using RxDB database with React Native is to check out the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/react-native",children:"RxDB React Native example"})," and use that as a tutorial."]}),"\n",(0,i.jsxs)(n.li,{children:["Check out the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB GitHub repository"})," and leave a star \u2b50 if you find it useful."]}),"\n",(0,i.jsxs)(n.li,{children:["Learn more about the ",(0,i.jsx)(n.a,{href:"/encryption.html",children:"RxDB encryption plugins"}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"By following these best practices and leveraging RxDB's powerful encryption plugins, you can build secure, performant, and robust React Native applications that keep your users' data safe."})]})}function h(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>o,x:()=>t});var r=s(6540);const i={},a=r.createContext(i);function o(e){const n=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),r.createElement(a.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/13dc6548.fadf8c1b.js b/docs/assets/js/13dc6548.fadf8c1b.js
deleted file mode 100644
index 4a5b1af38a5..00000000000
--- a/docs/assets/js/13dc6548.fadf8c1b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3483],{4637(e,a,t){t.r(a),t.d(a,{default:()=>r});var n=t(544),d=t(4848);function r(){return(0,n.default)({sem:{id:"gads",metaTitle:"The best Database on top of IndexedDB",title:(0,d.jsxs)(d.Fragment,{children:["The easiest way to ",(0,d.jsx)("b",{children:"store"})," and ",(0,d.jsx)("b",{children:"sync"})," Data in IndexedDB"]}),appName:"Browser",text:(0,d.jsx)(d.Fragment,{children:"Store data inside the Browsers IndexedDB to build high performance realtime applications that sync data from the backend and even work when offline."})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/14d72841.24eaace4.js b/docs/assets/js/14d72841.24eaace4.js
deleted file mode 100644
index 59c16c51caf..00000000000
--- a/docs/assets/js/14d72841.24eaace4.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9772],{5221(e,t,s){s.r(t),s.d(t,{default:()=>h});var l=s(4586),i=s(8711),r=s(6540),c=s(8141),n=s(4848);function h(){const{siteConfig:e}=(0,l.A)();return(0,r.useEffect)(()=>{(0,c.c)("get_newsletter",.4)}),(0,n.jsx)(i.A,{title:`Newsletter - ${e.title}`,description:"RxDB Newsletter",children:(0,n.jsx)("main",{children:(0,n.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,n.jsx)("a",{href:"/",children:(0,n.jsx)("div",{className:"logo",children:(0,n.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,n.jsx)("h1",{children:"RxDB Newsletter"}),(0,n.jsx)("p",{children:(0,n.jsx)("b",{children:"You will be redirected in a few seconds."})}),(0,n.jsx)("p",{children:(0,n.jsx)("a",{href:"http://eepurl.com/imD7WA",children:"Click here"})}),(0,n.jsx)("meta",{httpEquiv:"Refresh",content:"0; url=http://eepurl.com/imD7WA"})]})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/15f1e21f.306cc5e2.js b/docs/assets/js/15f1e21f.306cc5e2.js
deleted file mode 100644
index cf75fcc4d55..00000000000
--- a/docs/assets/js/15f1e21f.306cc5e2.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[970],{7990(e,a,s){s.r(a),s.d(a,{default:()=>i});var t=s(544),n=s(4848);function i(){return(0,t.default)({sem:{id:"gads",metaTitle:"The local Database for Vue.js Apps",appName:"Vue.js",title:(0,n.jsxs)(n.Fragment,{children:["The easiest way to ",(0,n.jsx)("b",{children:"store"})," and ",(0,n.jsx)("b",{children:"sync"})," Data in Vue.js"]}),text:(0,n.jsx)(n.Fragment,{children:"Store data inside of your Vue.js app to build high performance realtime applications that sync data from the backend and even work when offline."}),iconUrl:"/files/icons/vuejs.svg"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/17896441.476362fb.js b/docs/assets/js/17896441.476362fb.js
deleted file mode 100644
index fbcfb103346..00000000000
--- a/docs/assets/js/17896441.476362fb.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8401],{9057(e,t,s){s.d(t,{A:()=>r});var n=s(6540),i=s(8759),a=s(3230),l=s(8601),o=s(4848);const r={...i.A,code:function(e){return void 0!==e.children&&n.Children.toArray(e.children).every(e=>"string"==typeof e&&!e.includes("\n"))?(0,o.jsx)(a.A,{...e}):(0,o.jsx)("code",{...e})},pre:l.A}},9519(e,t,s){s.r(t),s.d(t,{default:()=>fe});var n=s(6540),i=s(5500),a=s(9532),l=s(4848);const o=n.createContext(null);function r({children:e,content:t}){const s=function(e){return(0,n.useMemo)(()=>({metadata:e.metadata,frontMatter:e.frontMatter,assets:e.assets,contentTitle:e.contentTitle,toc:e.toc}),[e])}(t);return(0,l.jsx)(o.Provider,{value:s,children:e})}function c(){const e=(0,n.useContext)(o);if(null===e)throw new a.dV("DocProvider");return e}function d(){const{metadata:e,frontMatter:t,assets:s}=c();return(0,l.jsx)(i.be,{title:e.title,description:e.description,keywords:t.keywords,image:s.image??t.image})}var h=s(4164),u=s(4581),m=s(1312),g=s(4584);function x(e){const{permalink:t,title:s,isNext:n}=e;return(0,l.jsxs)(g.$,{style:{textAlign:n?"right":"left",justifyContent:n?"right":"left",height:"auto",paddingTop:14,paddingBottom:14},href:t,children:[(0,l.jsx)("span",{style:{fontSize:"80%",display:"contents"},children:n?"Next":"Previous"}),(0,l.jsx)("br",{}),n?"":"\xab ",s,n?" \xbb":""]})}function b(e){const{className:t,previous:s,next:n}=e;return(0,l.jsxs)("nav",{className:(0,h.A)(t,"pagination-nav"),"aria-label":(0,m.T)({id:"theme.docs.paginator.navAriaLabel",message:"Docs pages",description:"The ARIA label for the docs pagination"}),children:[s&&(0,l.jsx)(x,{...s,subLabel:(0,l.jsx)(m.A,{id:"theme.docs.paginator.previous",description:"The label used to navigate to the previous doc",children:"Previous"})}),n&&(0,l.jsx)(x,{...n,subLabel:(0,l.jsx)(m.A,{id:"theme.docs.paginator.next",description:"The label used to navigate to the next doc",children:"Next"}),isNext:!0})]})}function v(){const{metadata:e}=c();return(0,l.jsx)(b,{className:"docusaurus-mt-lg",previous:e.previous,next:e.next})}var p=s(4586),j=s(8774),f=s(8295),A=s(7559),y=s(3886),N=s(3025);const T={unreleased:function({siteTitle:e,versionMetadata:t}){return(0,l.jsx)(m.A,{id:"theme.docs.versions.unreleasedVersionLabel",description:"The label used to tell the user that he's browsing an unreleased doc version",values:{siteTitle:e,versionLabel:(0,l.jsx)("b",{children:t.label})},children:"This is unreleased documentation for {siteTitle} {versionLabel} version."})},unmaintained:function({siteTitle:e,versionMetadata:t}){return(0,l.jsx)(m.A,{id:"theme.docs.versions.unmaintainedVersionLabel",description:"The label used to tell the user that he's browsing an unmaintained doc version",values:{siteTitle:e,versionLabel:(0,l.jsx)("b",{children:t.label})},children:"This is documentation for {siteTitle} {versionLabel}, which is no longer actively maintained."})}};function _(e){const t=T[e.versionMetadata.banner];return(0,l.jsx)(t,{...e})}function L({versionLabel:e,to:t,onClick:s}){return(0,l.jsx)(m.A,{id:"theme.docs.versions.latestVersionSuggestionLabel",description:"The label used to tell the user to check the latest version",values:{versionLabel:e,latestVersionLink:(0,l.jsx)("b",{children:(0,l.jsx)(j.A,{to:t,onClick:s,children:(0,l.jsx)(m.A,{id:"theme.docs.versions.latestVersionLinkLabel",description:"The label used for the latest version suggestion link label",children:"latest version"})})})},children:"For up-to-date documentation, see the {latestVersionLink} ({versionLabel})."})}function k({className:e,versionMetadata:t}){const{siteConfig:{title:s}}=(0,p.A)(),{pluginId:n}=(0,f.vT)({failfast:!0}),{savePreferredVersionName:i}=(0,y.g1)(n),{latestDocSuggestion:a,latestVersionSuggestion:o}=(0,f.HW)(n),r=a??(c=o).docs.find(e=>e.id===c.mainDocId);var c;return(0,l.jsxs)("div",{className:(0,h.A)(e,A.G.docs.docVersionBanner,"alert alert--warning margin-bottom--md"),role:"alert",children:[(0,l.jsx)("div",{children:(0,l.jsx)(_,{siteTitle:s,versionMetadata:t})}),(0,l.jsx)("div",{className:"margin-top--md",children:(0,l.jsx)(L,{versionLabel:o.label,to:r.path,onClick:()=>i(o.name)})})]})}function C({className:e}){const t=(0,N.r)();return t.banner?(0,l.jsx)(k,{className:e,versionMetadata:t}):null}function w({className:e}){const t=(0,N.r)();return t.badge?(0,l.jsx)("span",{className:(0,h.A)(e,A.G.docs.docVersionBadge,"badge badge--secondary"),children:(0,l.jsx)(m.A,{id:"theme.docs.versionBadge.label",values:{versionLabel:t.label},children:"Version: {versionLabel}"})}):null}const M="tag_zVej",H="tagRegular_sFm0",V="tagWithCount_h2kH";function B({permalink:e,label:t,count:s,description:n}){return(0,l.jsxs)(j.A,{rel:"tag",href:e,title:n,className:(0,h.A)(M,s?V:H),children:[t,s&&(0,l.jsx)("span",{children:s})]})}const G="tags_jXut",I="tag_QGVx";function R({tags:e}){return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)("b",{children:(0,l.jsx)(m.A,{id:"theme.tags.tagsListLabel",description:"The label alongside a tag list",children:"Tags:"})}),(0,l.jsx)("ul",{className:(0,h.A)(G,"padding--none","margin-left--sm"),children:e.map(e=>(0,l.jsx)("li",{className:I,children:(0,l.jsx)(B,{...e})},e.permalink))})]})}var F=s(2153);function S(){const{metadata:e}=c(),{editUrl:t,lastUpdatedAt:s,lastUpdatedBy:n,tags:i}=e,a=i.length>0,o=!!(t||s||n);return a||o?(0,l.jsxs)("footer",{className:(0,h.A)(A.G.docs.docFooter,"docusaurus-mt-lg"),children:[a&&(0,l.jsx)("div",{className:(0,h.A)("row margin-top--sm",A.G.docs.docFooterTagsRow),children:(0,l.jsx)("div",{className:"col",children:(0,l.jsx)(R,{tags:i})})}),o&&(0,l.jsx)(F.A,{className:(0,h.A)("margin-top--sm",A.G.docs.docFooterEditMetaRow),editUrl:t,lastUpdatedAt:s,lastUpdatedBy:n})]}):null}var z=s(1422),E=s(5195);const U="tocCollapsibleButton_TO0P",D="tocCollapsibleButtonExpanded_MG3E";function O({collapsed:e,...t}){return(0,l.jsx)("button",{type:"button",...t,className:(0,h.A)("clean-btn",U,!e&&D,t.className),children:(0,l.jsx)(m.A,{id:"theme.TOCCollapsible.toggleButtonLabel",description:"The label used by the button on the collapsible TOC component",children:"On this page"})})}const P="tocCollapsible_ETCw",W="tocCollapsibleContent_vkbj",$="tocCollapsibleExpanded_sAul";function J({toc:e,className:t,minHeadingLevel:s,maxHeadingLevel:n}){const{collapsed:i,toggleCollapsed:a}=(0,z.u)({initialState:!0});return(0,l.jsxs)("div",{className:(0,h.A)(P,!i&&$,t),children:[(0,l.jsx)(O,{collapsed:i,onClick:a}),(0,l.jsx)(z.N,{lazy:!0,className:W,collapsed:i,children:(0,l.jsx)(E.A,{toc:e,minHeadingLevel:s,maxHeadingLevel:n})})]})}const q="tocMobile_ITEo";function Q(){const{toc:e,frontMatter:t}=c();return(0,l.jsx)(J,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:(0,h.A)(A.G.docs.docTocMobile,q)})}var X=s(7763);function Y(){const{toc:e,frontMatter:t}=c();return(0,l.jsx)(X.A,{toc:e,minHeadingLevel:t.toc_min_heading_level,maxHeadingLevel:t.toc_max_heading_level,className:A.G.docs.docTocDesktop})}var Z=s(1107),K=s(7910);function ee({children:e}){const t=function(){const{metadata:e,frontMatter:t,contentTitle:s}=c();return t.hide_title||void 0!==s?null:e.title}();return(0,l.jsxs)("div",{className:(0,h.A)(A.G.docs.docMarkdown,"markdown"),children:[t&&(0,l.jsx)("header",{children:(0,l.jsx)(Z.A,{as:"h1",children:t})}),(0,l.jsx)(K.A,{children:e})]})}var te=s(4718),se=s(9169),ne=s(6025);function ie(e){return(0,l.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,l.jsx)("path",{d:"M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z",fill:"currentColor"})})}const ae="breadcrumbHomeIcon_YNFT";function le(){const e=(0,ne.Ay)("/");return(0,l.jsx)("li",{className:"breadcrumbs__item",children:(0,l.jsx)(j.A,{"aria-label":(0,m.T)({id:"theme.docs.breadcrumbs.home",message:"Home page",description:"The ARIA label for the home page in the breadcrumbs"}),className:"breadcrumbs__link",href:e,children:(0,l.jsx)(ie,{className:ae})})})}var oe=s(5260);function re(e){const t=function({breadcrumbs:e}){const{siteConfig:t}=(0,p.A)();return{"@context":"https://schema.org","@type":"BreadcrumbList",itemListElement:e.filter(e=>e.href).map((e,s)=>({"@type":"ListItem",position:s+1,name:e.label,item:`${t.url}${e.href}`}))}}({breadcrumbs:e.breadcrumbs});return(0,l.jsx)(oe.A,{children:(0,l.jsx)("script",{type:"application/ld+json",children:JSON.stringify(t)})})}const ce="breadcrumbsContainer_Z_bl";function de({children:e,href:t,isLast:s}){const n="breadcrumbs__link";return s?(0,l.jsx)("span",{className:n,children:e}):t?(0,l.jsx)(j.A,{className:n,href:t,children:(0,l.jsx)("span",{children:e})}):(0,l.jsx)("span",{className:n,children:e})}function he({children:e,active:t}){return(0,l.jsx)("li",{className:(0,h.A)("breadcrumbs__item",{"breadcrumbs__item--active":t}),children:e})}function ue(){const e=(0,te.OF)(),t=(0,se.Dt)();return e?(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(re,{breadcrumbs:e}),(0,l.jsx)("nav",{className:(0,h.A)(A.G.docs.docBreadcrumbs,ce),"aria-label":(0,m.T)({id:"theme.docs.breadcrumbs.navAriaLabel",message:"Breadcrumbs",description:"The ARIA label for the breadcrumbs"}),children:(0,l.jsxs)("ul",{className:"breadcrumbs",children:[t&&(0,l.jsx)(le,{}),e.map((t,s)=>{const n=s===e.length-1,i="category"===t.type&&t.linkUnlisted?void 0:t.href;return(0,l.jsx)(he,{active:n,children:(0,l.jsx)(de,{href:i,isLast:n,children:t.label})},s)})]})})]}):null}var me=s(6896);const ge="docItemContainer_c0TR",xe="docItemCol_z5aJ";var be=s(8141),ve=s(7810);function pe(e){const[t,s]=(0,n.useState)(!1),i={ul:{marginTop:25,listStyleType:"none"},li:{lineHeight:4,color:"var(--expo-theme-text-secondary)"},a:{color:"var(--fontColor-offwhite)"},img:{paddingRight:16,height:18,verticalAlign:"middle"},vote:{borderRadius:3,borderColor:"var(--fontColor-offwhite)",borderStyle:"solid",borderWidth:1,verticalAlign:"middle",padding:5,paddingLeft:8,paddingRight:8,textAlign:"center",justifyContent:"center",display:"inline-flex",marginLeft:20,cursor:"pointer"},down:{transform:"scale(1, -1)"},heart:{color:"var(--color-top)",display:"inline-block",transform:"scale(2)",paddingLeft:10}};let a=e.children.type.frontMatter.title?e.children.type.frontMatter.title:"";e.children.type.contentTitle&&e.children.type.contentTitle.length23){a=a.slice(0,23);const e=a.split(" ");e.pop(),a=e.join(" ")+"..."}function o(t){const n=e.children.type.metadata.slug,i="vote_"+(0,ve.dG)(n.split("/"))+"_"+t;console.log("vote: "+i),(0,be.c)(i,.1,1),s(!0)}return(0,l.jsxs)("ul",{style:i.ul,children:[t?(0,l.jsxs)("li",{style:i.li,children:["Thank you for your vote! ",(0,l.jsx)("div",{style:i.heart,children:"\u2665"})]}):(0,l.jsxs)("li",{style:i.li,children:["Was this page helpful?",(0,l.jsx)("div",{style:i.vote,children:(0,l.jsx)("img",{src:"/img/thumbs-up-white.svg",loading:"lazy",height:"14",onClick:()=>o("up")})}),(0,l.jsx)("div",{style:{...i.vote,...i.down},children:(0,l.jsx)("img",{src:"/img/thumbs-up-white.svg",loading:"lazy",height:"14",onClick:()=>o("down")})})]}),(0,l.jsx)("li",{children:(0,l.jsxs)("a",{href:"/chat/",target:"_blank",style:i.a,children:[(0,l.jsx)("img",{src:"/img/community-links/discord-logo.svg",style:i.img,loading:"lazy"}),"Ask a question on the forums about ",a]})})]})}function je(e){const t=function(){const{frontMatter:e,toc:t}=c(),s=(0,u.l)(),n=e.hide_table_of_contents,i=!n&&t.length>0;return{hidden:n,mobile:i?(0,l.jsx)(Q,{}):void 0,desktop:!i||"desktop"!==s&&"ssr"!==s?void 0:(0,l.jsx)(Y,{})}}(),{metadata:s}=c();return(0,l.jsxs)("div",{className:"row",children:[(0,l.jsxs)("div",{className:(0,h.A)("col",!t.hidden&&xe),children:[(0,l.jsx)(me.A,{metadata:s}),(0,l.jsx)(C,{}),(0,l.jsxs)("div",{className:ge,children:[(0,l.jsxs)("article",{children:[(0,l.jsx)(ue,{}),(0,l.jsx)(w,{}),t.mobile,(0,l.jsx)(ee,{children:e.children}),(0,l.jsx)(S,{})]}),(0,l.jsx)(pe,{...e}),(0,l.jsx)(v,{})]})]}),t.desktop&&(0,l.jsx)("div",{className:"col col--3",children:t.desktop})]})}function fe(e){const t=`docs-doc-id-${e.content.metadata.id}`,s=e.content;return(0,l.jsx)(r,{content:e.content,children:(0,l.jsxs)(i.e3,{className:t,children:[(0,l.jsx)(d,{}),(0,l.jsx)(je,{children:(0,l.jsx)(s,{})})]})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/187b985e.4ee7b6e5.js b/docs/assets/js/187b985e.4ee7b6e5.js
deleted file mode 100644
index 845858d6319..00000000000
--- a/docs/assets/js/187b985e.4ee7b6e5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6866],{3247(e,n,s){s.d(n,{g:()=>i});var r=s(4848);function i(e){const n=[];let s=null;return e.children.forEach(e=>{e.props.id?(s&&n.push(s),s={headline:e,paragraphs:[]}):s&&s.paragraphs.push(e)}),s&&n.push(s),(0,r.jsx)("div",{style:o.stepsContainer,children:n.map((e,n)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:n+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,n)=>(0,r.jsx)("div",{style:o.item,children:e},n))]})]},n))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},8243(e,n,s){s.r(n),s.d(n,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>t,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"replication-webrtc","title":"WebRTC P2P Replication with RxDB - Sync Browsers and Devices","description":"Learn to set up peer-to-peer WebRTC replication with RxDB. Bypass central servers and enjoy secure, low-latency data sync across all clients.","source":"@site/docs/replication-webrtc.md","sourceDirName":".","slug":"/replication-webrtc.html","permalink":"/replication-webrtc.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"WebRTC P2P Replication with RxDB - Sync Browsers and Devices","slug":"replication-webrtc.html","description":"Learn to set up peer-to-peer WebRTC replication with RxDB. Bypass central servers and enjoy secure, low-latency data sync across all clients."},"sidebar":"tutorialSidebar","previous":{"title":"CouchDB Replication","permalink":"/replication-couchdb.html"},"next":{"title":"Firestore Replication","permalink":"/replication-firestore.html"}}');var i=s(4848),o=s(8453),a=s(3247);const t={title:"WebRTC P2P Replication with RxDB - Sync Browsers and Devices",slug:"replication-webrtc.html",description:"Learn to set up peer-to-peer WebRTC replication with RxDB. Bypass central servers and enjoy secure, low-latency data sync across all clients."},l="P2P WebRTC Replication with RxDB - Sync Data between Browsers and Devices in JavaScript",c={},d=[{value:"What is WebRTC?",id:"what-is-webrtc",level:2},{value:"Benefits of P2P Sync with WebRTC Compared to Client-Server Architecture",id:"benefits-of-p2p-sync-with-webrtc-compared-to-client-server-architecture",level:2},{value:"Peer-to-Peer (P2P) WebRTC Replication with the RxDB JavaScript Database",id:"peer-to-peer-p2p-webrtc-replication-with-the-rxdb-javascript-database",level:2},{value:"Using RxDB with the WebRTC Replication Plugin",id:"using-rxdb-with-the-webrtc-replication-plugin",level:2},{value:"Create the Database and Collection",id:"create-the-database-and-collection",level:3},{value:"Import the WebRTC replication plugin",id:"import-the-webrtc-replication-plugin",level:3},{value:"Start the P2P replication",id:"start-the-p2p-replication",level:3},{value:"Observe Errors",id:"observe-errors",level:3},{value:"Stop the Replication",id:"stop-the-replication",level:3},{value:"Live replications",id:"live-replications",level:2},{value:"Signaling Server",id:"signaling-server",level:2},{value:"Peer Validation",id:"peer-validation",level:2},{value:"Conflict detection in WebRTC replication",id:"conflict-detection-in-webrtc-replication",level:2},{value:"Known problems",id:"known-problems",level:2},{value:"SimplePeer requires to have process.nextTick()",id:"simplepeer-requires-to-have-processnexttick",level:3},{value:"Polyfill the WebSocket and WebRTC API in Node.js",id:"polyfill-the-websocket-and-webrtc-api-in-nodejs",level:3},{value:"Storing replicated data encrypted on client device",id:"storing-replicated-data-encrypted-on-client-device",level:2},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"p2p-webrtc-replication-with-rxdb---sync-data-between-browsers-and-devices-in-javascript",children:"P2P WebRTC Replication with RxDB - Sync Data between Browsers and Devices in JavaScript"})}),"\n",(0,i.jsxs)(n.p,{children:["WebRTC P2P data connections are revolutionizing real-time web and mobile development by ",(0,i.jsx)(n.strong,{children:"eliminating central servers"})," in scenarios where clients can communicate directly. With the ",(0,i.jsx)(n.strong,{children:"RxDB"})," ",(0,i.jsx)(n.a,{href:"/replication.html",children:"Sync Engine"}),", you can sync your local database state across multiple browsers or devices via ",(0,i.jsx)(n.strong,{children:"WebRTC P2P (Peer-to-Peer)"})," connections, ensuring scalable, secure, and ",(0,i.jsx)(n.strong,{children:"low-latency"})," data flows without traditional server bottlenecks."]}),"\n",(0,i.jsx)(n.h2,{id:"what-is-webrtc",children:"What is WebRTC?"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API",children:"WebRTC"})," stands for Web ",(0,i.jsx)(n.a,{href:"/articles/realtime-database.html",children:"Real-Time"})," Communication. It is an open standard that enables browsers and native apps to exchange audio, video, or ",(0,i.jsx)(n.strong,{children:"arbitrary data"})," directly between peers, bypassing a central server after the initial connection is established. WebRTC uses NAT traversal techniques like ",(0,i.jsx)(n.a,{href:"https://developer.liveswitch.io/liveswitch-server/guides/what-are-stun-turn-and-ice.html",children:"ICE"})," (Interactive Connectivity Establishment) to punch through firewalls and establish direct links. This peer-to-peer nature drastically reduces latency while maintaining ",(0,i.jsx)(n.strong,{children:"high security"})," and ",(0,i.jsx)(n.strong,{children:"end-to-end encryption"})," capabilities."]}),"\n",(0,i.jsxs)(n.p,{children:["For a deeper look at comparing WebRTC with ",(0,i.jsx)(n.strong,{children:"WebSockets"})," and ",(0,i.jsx)(n.strong,{children:"WebTransport"}),", you can read our ",(0,i.jsx)(n.a,{href:"/articles/websockets-sse-polling-webrtc-webtransport.html",children:"comprehensive overview"}),". While WebSockets or WebTransport often work in client-server contexts, WebRTC offers direct peer-to-peer connections ideal for fully decentralized data flows."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://webrtc.org/",target:"_blank",children:(0,i.jsx)("img",{src:"/files/icons/webrtc.svg",alt:"WebRTC",width:"80"})})}),"\n",(0,i.jsx)(n.h2,{id:"benefits-of-p2p-sync-with-webrtc-compared-to-client-server-architecture",children:"Benefits of P2P Sync with WebRTC Compared to Client-Server Architecture"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Reduced Latency"})," - By skipping a central server hop, data travels directly from one client to another, minimizing round-trip times and improving responsiveness."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Scalability"})," - New peers can join without overloading a central infrastructure. The sync overhead increases linearly with the number of connections rather than requiring a massive server cluster."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Privacy & Ownership"})," - Data stays within the user\u2019s devices, avoiding risks tied to storing data on third-party servers. This design aligns well with ",(0,i.jsx)(n.a,{href:"/articles/local-first-future.html",children:"local-first"}),' or "',(0,i.jsx)(n.a,{href:"/articles/zero-latency-local-first.html",children:"zero-latency"}),'" apps.']}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Resilience"})," - In some scenarios, if the central server is unreachable, P2P connections remain operational (assuming a functioning signaling path). Apps can still replicate data among local networks like when they are in the same Wifi or LAN."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Cost Savings"})," - Reducing the reliance on a high-bandwidth server can cut hosting and bandwidth expenses, particularly in high-traffic or IoT-style use cases."]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"/files/logo/rxdb_javascript_database.svg",alt:"JavaScript Embedded Database",width:"220"})})}),"\n",(0,i.jsx)(n.h2,{id:"peer-to-peer-p2p-webrtc-replication-with-the-rxdb-javascript-database",children:"Peer-to-Peer (P2P) WebRTC Replication with the RxDB JavaScript Database"}),"\n",(0,i.jsxs)(n.p,{children:["Traditionally, real-time data synchronization depends on ",(0,i.jsx)(n.strong,{children:"centralized servers"})," to manage and distribute updates. In contrast, RxDB\u2019s WebRTC P2P replication allows data to flow ",(0,i.jsx)(n.strong,{children:"directly"})," among clients, removing the server as a data store. This approach is ",(0,i.jsx)(n.strong,{children:"live"})," and ",(0,i.jsx)(n.strong,{children:"fully decentralized"}),", requiring only a ",(0,i.jsx)(n.a,{href:"#signaling-server",children:"signaling server"})," for initial discovery:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"No master-slave"})," concept - each peer hosts its own local RxDB."]}),"\n",(0,i.jsxs)(n.li,{children:["Clients (",(0,i.jsx)(n.a,{href:"/articles/browser-database.html",children:"browsers"}),", devices) connect to each other via WebRTC data channels."]}),"\n",(0,i.jsxs)(n.li,{children:["The ",(0,i.jsx)(n.a,{href:"/replication.html",children:"RxDB replication protocol"})," then handles pushing/pulling document changes across peers."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Because RxDB is a NoSQL database and the replication protocol is straightforward, setting up robust P2P sync is far ",(0,i.jsx)(n.strong,{children:"easier"})," than orchestrating a complex client-server database architecture."]}),"\n",(0,i.jsx)(n.h2,{id:"using-rxdb-with-the-webrtc-replication-plugin",children:"Using RxDB with the WebRTC Replication Plugin"}),"\n",(0,i.jsxs)(n.p,{children:["Before you use this plugin, make sure that you understand how ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API",children:"WebRTC works"}),". Here we build a todo-app that replicates todo-entries between clients:"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"https://github.com/pubkey/rxdb-quickstart/raw/master/files/p2p-todo-demo.gif",alt:"JavaScript Embedded Database",width:"500"})})}),"\n",(0,i.jsxs)(n.p,{children:["You can find a fully build example of this at the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb-quickstart",children:"RxDB Quickstart Repository"})," which you can also ",(0,i.jsx)(n.a,{href:"https://pubkey.github.io/rxdb-quickstart/",children:"try out online"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["Four you create the ",(0,i.jsx)(n.a,{href:"/rx-database.html",children:"database"})," and then you can configure the replication:"]}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(n.h3,{id:"create-the-database-and-collection",children:"Create the Database and Collection"}),(0,i.jsxs)(n.p,{children:["Here we create a database with the ",(0,i.jsx)(n.a,{href:"/rx-storage-localstorage.html",children:"localstorage"})," based storage that stores data inside of the ",(0,i.jsx)(n.a,{href:"/articles/localstorage.html",children:"LocalStorage API"})," in a browser. RxDB has a wide ",(0,i.jsx)(n.a,{href:"/rx-storage.html",children:"range of storages"})," for other JavaScript runtimes."]}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myTodoDB'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" todos"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'todo schema'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'boolean'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" default"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" created"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" format"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'date-time'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'title'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'done'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert an example document"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"todos"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'todo-1'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'P2P demo task'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" created"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".toISOString"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(n.h3,{id:"import-the-webrtc-replication-plugin",children:"Import the WebRTC replication plugin"}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicateWebRTC"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getConnectionHandlerSimplePeer"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-webrtc'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(n.h3,{id:"start-the-p2p-replication",children:"Start the P2P replication"}),(0,i.jsxs)(n.p,{children:["To start the replication you have to call ",(0,i.jsx)(n.code,{children:"replicateWebRTC"})," on the ",(0,i.jsx)(n.a,{href:"/rx-collection.html",children:"collection"}),"."]}),(0,i.jsxs)(n.p,{children:["As options you have to provide a ",(0,i.jsx)(n.code,{children:"topic"})," and a connection handler function that implements the ",(0,i.jsx)(n.code,{children:"P2PConnectionHandlerCreator"})," interface. As default you should start with the ",(0,i.jsx)(n.code,{children:"getConnectionHandlerSimplePeer"})," method which uses the ",(0,i.jsx)(n.a,{href:"https://github.com/feross/simple-peer",children:"simple-peer"})," library and comes shipped with RxDB."]}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationPool"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateWebRTC"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Start the replication for a single collection"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".todos"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // The topic is like a 'room-name'. All clients with the same topic"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // will replicate with each other. In most cases you want to use"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // a different topic string per user. Also you should prefix the topic with"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // a unique identifier for your app, to ensure you do not let your users connect"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // with other apps that also use the RxDB P2P Replication."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" topic"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-users-pool'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * You need a collection handler to be able to create WebRTC connections."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here we use the simple peer handler which uses the 'simple-peer' npm library."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * To learn how to create a custom connection handler, read the source code,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * it is pretty simple."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" connectionHandlerCreator"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getConnectionHandlerSimplePeer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Set the signaling server url."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // You can use the server provided by RxDB for tryouts,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // but in production you should use your own server instead."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" signalingServerUrl"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'wss://signaling.rxdb.info/'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // only in Node.js, we need the wrtc library"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // because Node.js does not contain the WebRTC API."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" wrtc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'node-datachannel/polyfill'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // only in Node.js, we need the WebSocket library"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // because Node.js does not contain the WebSocket API."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" webSocketConstructor"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'ws'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:").WebSocket"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),(0,i.jsxs)(n.p,{children:["Notice that in difference to the other ",(0,i.jsx)(n.a,{href:"/replication.html",children:"replication plugins"}),", the WebRTC replication returns a ",(0,i.jsx)(n.code,{children:"replicationPool"})," instead of a single ",(0,i.jsx)(n.code,{children:"RxReplicationState"}),". The ",(0,i.jsx)(n.code,{children:"replicationPool"})," contains all replication states of the connected peers in the P2P network."]}),(0,i.jsx)(n.h3,{id:"observe-errors",children:"Observe Errors"}),(0,i.jsxs)(n.p,{children:["To ensure we log out potential errors, observe the ",(0,i.jsx)(n.code,{children:"error$"})," observable of the pool."]}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationPool"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"error$"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(err "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".error"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'WebRTC Error:'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" err));"})]})})})}),(0,i.jsx)(n.h3,{id:"stop-the-replication",children:"Stop the Replication"}),(0,i.jsx)(n.p,{children:"You can also dynamically stop the replication."}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationPool"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cancel"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})})]}),"\n",(0,i.jsx)(n.h2,{id:"live-replications",children:"Live replications"}),"\n",(0,i.jsxs)(n.p,{children:["The WebRTC replication is ",(0,i.jsx)(n.strong,{children:"always live"})," because there can not be a one-time sync when it is always possible to have new Peers that join the connection pool. Therefore you cannot set the ",(0,i.jsx)(n.code,{children:"live: false"})," option like in the other replication plugins."]}),"\n",(0,i.jsx)(n.h2,{id:"signaling-server",children:"Signaling Server"}),"\n",(0,i.jsxs)(n.p,{children:["For P2P replication to work with the RxDB WebRTC Replication Plugin, a ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Signaling_and_video_calling",children:"signaling server"})," is required. The signaling server helps peers discover each other and establish connections."]}),"\n",(0,i.jsx)(n.p,{children:"RxDB ships with a default signaling server that can be used with the simple-peer connection handler. This server is made for demonstration purposes and tryouts. It is not reliable and might be offline at any time.\nIn production you must always use your own signaling server instead!"}),"\n",(0,i.jsx)(n.p,{children:"Creating a basic signaling server is straightforward. The provided example uses 'socket.io' for WebSocket communication. However, in production, you'd want to create a more robust signaling server with authentication and additional logic to suit your application's needs."}),"\n",(0,i.jsxs)(n.p,{children:["Here is a quick example implementation of a signaling server that can be used with the connection handler from ",(0,i.jsx)(n.code,{children:"getConnectionHandlerSimplePeer()"}),":"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" startSignalingServerSimplePeer"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-webrtc'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" serverState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" startSignalingServerSimplePeer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" port"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 8080"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- port"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["For custom signaling servers with more complex logic, you can check the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/src/plugins/replication-webrtc/signaling-server.ts",children:"source code of the default one"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"peer-validation",children:"Peer Validation"}),"\n",(0,i.jsxs)(n.p,{children:["By default the replication will replicate with every peer the signaling server tells them about.\nYou can prevent invalid peers from replication by passing a custom ",(0,i.jsx)(n.code,{children:"isPeerValid()"})," function that either returns ",(0,i.jsx)(n.code,{children:"true"})," on valid peers and ",(0,i.jsx)(n.code,{children:"false"})," on invalid peers."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationPool"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateWebRTC"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" isPeerValid"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (peer) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull: {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"conflict-detection-in-webrtc-replication",children:"Conflict detection in WebRTC replication"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB's conflict handling works by detecting and resolving conflicts that may arise when multiple clients in a decentralized database system attempt to modify the same data concurrently.\nA ",(0,i.jsx)(n.strong,{children:"custom conflict handler"})," can be set up, which is a plain JavaScript function. The conflict handler is run on each replicated document write and resolves the conflict if required. ",(0,i.jsx)(n.a,{href:"https://rxdb.info/transactions-conflicts-revisions.html",children:"Find out more about RxDB conflict handling here"})]}),"\n",(0,i.jsx)(n.h2,{id:"known-problems",children:"Known problems"}),"\n",(0,i.jsxs)(n.h3,{id:"simplepeer-requires-to-have-processnexttick",children:["SimplePeer requires to have ",(0,i.jsx)(n.code,{children:"process.nextTick()"})]}),"\n",(0,i.jsxs)(n.p,{children:["In the browser you might not have a process variable or process.nextTick() method. But the ",(0,i.jsx)(n.a,{href:"https://github.com/feross/simple-peer",children:"simple peer"})," uses that so you have to polyfill it."]}),"\n",(0,i.jsxs)(n.p,{children:["In webpack you can use the ",(0,i.jsx)(n.code,{children:"process/browser"})," package to polyfill it:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" plugins"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" webpack"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".ProvidePlugin"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" process"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'process/browser'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"];"})})]})})}),"\n",(0,i.jsx)(n.p,{children:"In angular or other libraries you can add the polyfill manually:"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"window"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".process "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" nextTick"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (fn"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ..."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"args) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fn"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"..."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"args))"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsx)(n.h3,{id:"polyfill-the-websocket-and-webrtc-api-in-nodejs",children:"Polyfill the WebSocket and WebRTC API in Node.js"}),"\n",(0,i.jsxs)(n.p,{children:["While all modern browsers support the WebRTC and WebSocket APIs, they is missing in Node.js which will throw the error ",(0,i.jsx)(n.code,{children:"No WebRTC support: Specify opts.wrtc option in this environment"}),". Therefore you have to polyfill it with a compatible WebRTC and WebSocket polyfill. It is recommended to use the ",(0,i.jsx)(n.a,{href:"https://github.com/murat-dogan/node-datachannel/tree/master/src/polyfill",children:"node-datachannel package"})," for WebRTC which ",(0,i.jsx)(n.strong,{children:"does not"})," come with RxDB but has to be installed before via ",(0,i.jsx)(n.code,{children:"npm install node-datachannel --save"}),".\nFor the Websocket API use the ",(0,i.jsx)(n.code,{children:"ws"})," package that is included into RxDB."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" nodeDatachannelPolyfill "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'node-datachannel/polyfill'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { WebSocket } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'ws'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationPool"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateWebRTC"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" connectionHandlerCreator"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getConnectionHandlerSimplePeer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" signalingServerUrl"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'wss://example.com:8080'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" wrtc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" nodeDatachannelPolyfill"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" webSocketConstructor"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" WebSocket"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"storing-replicated-data-encrypted-on-client-device",children:"Storing replicated data encrypted on client device"}),"\n",(0,i.jsxs)(n.p,{children:["Storing replicated data encrypted on client devices using the RxDB Encryption Plugin is a pivotal step towards bolstering ",(0,i.jsx)(n.strong,{children:"data security"})," and ",(0,i.jsx)(n.strong,{children:"user privacy"}),".\nThe WebRTC replication plugin seamlessly integrates with the ",(0,i.jsx)(n.a,{href:"/encryption.html",children:"RxDB encryption plugins"}),", providing a robust solution for encrypting sensitive information before it's stored locally. By doing so, it ensures that even if unauthorized access to the device occurs, the data remains protected and unintelligible without the encryption key (or password). This approach is particularly vital in scenarios where user-generated content or confidential data is replicated across devices, as it empowers users with control over their own data while adhering to stringent security standards. ",(0,i.jsx)(n.a,{href:"/encryption.html",children:"Read more about the encryption plugins here"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsxs)(n.strong,{children:["Check out the ",(0,i.jsx)(n.a,{href:"/quickstart.html",children:"RxDB Quickstart"})]})," to see how to set up your first RxDB database."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Explore advanced features"})," like ",(0,i.jsx)(n.a,{href:"/transactions-conflicts-revisions.html",children:"Custom Conflict Handling"})," or ",(0,i.jsx)(n.a,{href:"/rx-storage-performance.html",children:"Offline-First Performance"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Try an example"})," at ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb-quickstart",children:"RxDB Quickstart GitHub"})," to see a working P2P Sync setup."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Join the RxDB Community"})," on ",(0,i.jsx)(n.a,{href:"/code/",children:"GitHub"})," or ",(0,i.jsx)(n.a,{href:"/chat/",children:"Discord"})," if you have questions or want to share your P2P WebRTC experiences."]}),"\n"]})]})}function p(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,n,s){s.d(n,{R:()=>a,x:()=>t});var r=s(6540);const i={},o=r.createContext(i);function a(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1b0f8c91.21118c24.js b/docs/assets/js/1b0f8c91.21118c24.js
deleted file mode 100644
index 6d238854674..00000000000
--- a/docs/assets/js/1b0f8c91.21118c24.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3129],{5787(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"backup","title":"Backup","description":"Easily back up your RxDB database to JSON files and attachments on the filesystem with the Backup Plugin - ensuring reliable Node.js data protection.","source":"@site/docs/backup.md","sourceDirName":".","slug":"/backup.html","permalink":"/backup.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Backup","slug":"backup.html","description":"Easily back up your RxDB database to JSON files and attachments on the filesystem with the Backup Plugin - ensuring reliable Node.js data protection."},"sidebar":"tutorialSidebar","previous":{"title":"Cleanup","permalink":"/cleanup.html"},"next":{"title":"Leader Election","permalink":"/leader-election.html"}}');var a=s(4848),r=s(8453);const t={title:"Backup",slug:"backup.html",description:"Easily back up your RxDB database to JSON files and attachments on the filesystem with the Backup Plugin - ensuring reliable Node.js data protection."},o="\ud83d\udce5 Backup Plugin",l={},c=[{value:"Installation",id:"installation",level:2},{value:"one-time backup",id:"one-time-backup",level:2},{value:"live backup",id:"live-backup",level:2},{value:"writeEvents$",id:"writeevents",level:2},{value:"Limitations",id:"limitations",level:2}];function d(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"-backup-plugin",children:"\ud83d\udce5 Backup Plugin"})}),"\n",(0,a.jsx)(n.p,{children:"With the backup plugin you can write the current database state and ongoing changes into folders on the filesystem.\nThe files are written in plain json together with their attachments so that you can read them out with any software or tools, without being bound to RxDB."}),"\n",(0,a.jsx)(n.p,{children:"This is useful to:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Consume the database content with other software that cannot replicate with RxDB"}),"\n",(0,a.jsx)(n.li,{children:"Write a backup of the database to a remote server by mounting the backup folder on the other server."}),"\n"]}),"\n",(0,a.jsxs)(n.p,{children:["The backup plugin works only in node.js, not in a browser. It is intended to have a backup strategy when using RxDB on the server side like with the ",(0,a.jsx)(n.a,{href:"/rx-server.html",children:"RxServer"}),". To run backups on the client side, you should use one of the ",(0,a.jsx)(n.a,{href:"/replication.html",children:"replication"})," plugins instead."]}),"\n",(0,a.jsx)(n.h2,{id:"installation",children:"Installation"}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBBackupPlugin } "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/backup'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBBackupPlugin);"})]})]})})}),"\n",(0,a.jsx)(n.h2,{id:"one-time-backup",children:"one-time backup"}),"\n",(0,a.jsxs)(n.p,{children:["Write the whole database to the filesystem ",(0,a.jsx)(n.strong,{children:"once"}),".\nWhen called multiple times, it will continue from the last checkpoint and not start all over again."]}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupOptions"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // if false, a one-time backup will be written"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // the folder where the backup will be stored"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" directory"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/my-backup-folder/'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // if true, attachments will also be saved"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".backup"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(backupOptions);"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".awaitInitialBackup"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// call again to run from the last checkpoint"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState2"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".backup"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(backupOptions);"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState2"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".awaitInitialBackup"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,a.jsx)(n.h2,{id:"live-backup",children:"live backup"}),"\n",(0,a.jsxs)(n.p,{children:["When ",(0,a.jsx)(n.code,{children:"live: true"})," is set, the backup will write all ongoing changes to the backup directory."]}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupOptions"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // set live: true to have an ongoing backup"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" directory"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/my-backup-folder/'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".backup"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(backupOptions);"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// you can still await the initial backup write, but further changes will still be processed."})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".awaitInitialBackup"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,a.jsx)(n.h2,{id:"writeevents",children:"writeEvents$"}),"\n",(0,a.jsxs)(n.p,{children:["You can listen to the ",(0,a.jsx)(n.code,{children:"writeEvents$"})," Observable to get notified about written backup files."]}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupOptions"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" directory"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/my-backup-folder/'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".backup"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(backupOptions);"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" subscription"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" backupState"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"writeEvents$"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(writeEvent "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(writeEvent));"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/*"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"> {"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" collectionName: 'humans',"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" documentId: 'foobar',"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" files: ["})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" '/my-backup-folder/foobar/document.json'"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" ],"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" deleted: false"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"}"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"*/"})})]})})}),"\n",(0,a.jsx)(n.h2,{id:"limitations",children:"Limitations"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"It is currently not possible to import from a written backup. If you need this functionality, please make a pull request."}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>t,x:()=>o});var i=s(6540);const a={},r=i.createContext(a);function t(e){const n=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:t(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1b238727.c81ff6e0.js b/docs/assets/js/1b238727.c81ff6e0.js
deleted file mode 100644
index c8cb9a1792a..00000000000
--- a/docs/assets/js/1b238727.c81ff6e0.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4013],{2493(e,r,s){s.r(r),s.d(r,{assets:()=>c,contentTitle:()=>i,default:()=>m,frontMatter:()=>a,metadata:()=>t,toc:()=>l});const t=JSON.parse('{"id":"rx-storage-performance","title":"\ud83d\udcc8 Discover RxDB Storage Benchmarks","description":"Explore real-world benchmarks comparing RxDB\'s persistent and semi-persistent storages. Discover which storage option delivers the fastest performance.","source":"@site/docs/rx-storage-performance.md","sourceDirName":".","slug":"/rx-storage-performance.html","permalink":"/rx-storage-performance.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"\ud83d\udcc8 Discover RxDB Storage Benchmarks","slug":"rx-storage-performance.html","description":"Explore real-world benchmarks comparing RxDB\'s persistent and semi-persistent storages. Discover which storage option delivers the fastest performance."},"sidebar":"tutorialSidebar","previous":{"title":"React","permalink":"/react.html"},"next":{"title":"NoSQL Performance Tips","permalink":"/nosql-performance-tips.html"}}');var n=s(4848),o=s(8453);const a={title:"\ud83d\udcc8 Discover RxDB Storage Benchmarks",slug:"rx-storage-performance.html",description:"Explore real-world benchmarks comparing RxDB's persistent and semi-persistent storages. Discover which storage option delivers the fastest performance."},i=void 0,c={},l=[{value:"RxStorage Performance comparison",id:"rxstorage-performance-comparison",level:2},{value:"Persistent vs Semi-Persistent storages",id:"persistent-vs-semi-persistent-storages",level:2},{value:"Performance comparison",id:"performance-comparison",level:2},{value:"Measurements",id:"measurements",level:3},{value:"Browser based Storages Performance Comparison",id:"browser-based-storages-performance-comparison",level:2},{value:"Node/Native based Storages Performance Comparison",id:"nodenative-based-storages-performance-comparison",level:2}];function d(e){const r={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(r.h2,{id:"rxstorage-performance-comparison",children:"RxStorage Performance comparison"}),"\n",(0,n.jsxs)(r.p,{children:["A big difference in the RxStorage implementations is the ",(0,n.jsx)(r.strong,{children:"performance"}),". In difference to a server side database, RxDB is bound to the limits of the JavaScript runtime and depending on the runtime, there are different possibilities to store and fetch data. For example in the browser it is only possible to store data in a ",(0,n.jsx)(r.a,{href:"/slow-indexeddb.html",children:"slow IndexedDB"})," or OPFS instead of a filesystem while on React-Native you can use the ",(0,n.jsx)(r.a,{href:"/rx-storage-sqlite.html",children:"SQLite storage"}),"."]}),"\n",(0,n.jsxs)(r.p,{children:["Therefore the performance can be completely different depending on where you use RxDB and what you do with it. Here you can see some performance measurements and descriptions on how the different ",(0,n.jsx)(r.a,{href:"/rx-storage.html",children:"storages"})," work and how their performance is different."]}),"\n",(0,n.jsx)(r.h2,{id:"persistent-vs-semi-persistent-storages",children:"Persistent vs Semi-Persistent storages"}),"\n",(0,n.jsx)(r.p,{children:'The "normal" storages are always persistent. This means each RxDB write is directly written to disc and all queries run on the disc state. This means a good startup performance because nothing has to be done on startup.'}),"\n",(0,n.jsxs)(r.p,{children:["In contrast, semi-persistent storages like ",(0,n.jsx)(r.a,{href:"/rx-storage-memory-mapped.html",children:"memory mapped"})," store all data in memory on startup and only save to disc occasionally (or on exit). Therefore it has a very fast read/write performance, but loading all data into memory on the first page load can take longer for big amounts of documents. Also these storages can only be used when all data fits into the memory at least once. In general it is recommended to stay on the persistent storages and only use semi-persistent ones, when you know for sure that the dataset will stay small (less than 2k documents)."]}),"\n",(0,n.jsx)(r.h2,{id:"performance-comparison",children:"Performance comparison"}),"\n",(0,n.jsx)(r.p,{children:"In the following you can find some performance measurements and comparisons. Notice that these are only a small set of possible RxDB operations. If performance is really relevant for your use case, you should do your own measurements with usage-patterns that are equal to how you use RxDB in production."}),"\n",(0,n.jsx)(r.h3,{id:"measurements",children:"Measurements"}),"\n",(0,n.jsx)(r.p,{children:"Here the following metrics are measured:"}),"\n",(0,n.jsxs)(r.ul,{children:["\n",(0,n.jsxs)(r.li,{children:["time-to-first-insert: Many storages run lazy, so it makes no sense to compare the time which is required to create a database with collections. Instead we measure the ",(0,n.jsx)(r.strong,{children:"time-to-first-insert"})," which is the whole timespan from database creation until the first single document write is done."]}),"\n",(0,n.jsx)(r.li,{children:"insert documents (bulk): Insert 500 documents with a single bulk-insert operation."}),"\n",(0,n.jsxs)(r.li,{children:["find documents by id (bulk): Here we fetch 100% of the stored documents with a single ",(0,n.jsx)(r.code,{children:"findByIds()"})," call."]}),"\n",(0,n.jsx)(r.li,{children:"insert documents (serial): Insert 50 documents, one after each other."}),"\n",(0,n.jsxs)(r.li,{children:["find documents by id (serial): Here we find 50 documents in serial with one ",(0,n.jsx)(r.code,{children:"findByIds()"})," call per document."]}),"\n",(0,n.jsxs)(r.li,{children:["find documents by query: Here we fetch 100% of the stored documents with a single ",(0,n.jsx)(r.code,{children:"find()"})," call."]}),"\n",(0,n.jsxs)(r.li,{children:["find documents by query: Here we fetch all of the stored documents with a 4 ",(0,n.jsx)(r.code,{children:"find()"})," calls that run in parallel. Each fetching 25% of the documents."]}),"\n",(0,n.jsxs)(r.li,{children:["count documents: Counts 100% of the stored documents with a single ",(0,n.jsx)(r.code,{children:"count()"})," call. Here we measure 4 runs at once to have a higher number that is easier to compare."]}),"\n"]}),"\n",(0,n.jsx)(r.h2,{id:"browser-based-storages-performance-comparison",children:"Browser based Storages Performance Comparison"}),"\n",(0,n.jsxs)(r.p,{children:["The performance patterns of the browser based storages are very diverse. The ",(0,n.jsx)(r.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB storage"})," is recommended for mostly all use cases so you should start with that one. Later you can do performance testings and switch to another storage like ",(0,n.jsx)(r.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," or ",(0,n.jsx)(r.a,{href:"/rx-storage-memory-mapped.html",children:"memory-mapped"}),"."]}),"\n",(0,n.jsx)("p",{align:"center",children:(0,n.jsx)("img",{src:"./files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"700"})}),"\n",(0,n.jsx)(r.h2,{id:"nodenative-based-storages-performance-comparison",children:"Node/Native based Storages Performance Comparison"}),"\n",(0,n.jsxs)(r.p,{children:["For most client-side native applications (",(0,n.jsx)(r.a,{href:"/react-native-database.html",children:"react-native"}),", ",(0,n.jsx)(r.a,{href:"/electron-database.html",children:"electron"}),", ",(0,n.jsx)(r.a,{href:"/capacitor-database.html",children:"capacitor"}),"), using the ",(0,n.jsx)(r.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," is recommended. For non-client side applications like a server, use the ",(0,n.jsx)(r.a,{href:"/rx-storage-mongodb.html",children:"MongoDB storage"})," instead."]}),"\n",(0,n.jsx)("p",{align:"center",children:(0,n.jsx)("img",{src:"./files/rx-storage-performance-node.png",alt:"RxStorage performance - Node.js",width:"700"})})]})}function m(e={}){const{wrapper:r}={...(0,o.R)(),...e.components};return r?(0,n.jsx)(r,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453(e,r,s){s.d(r,{R:()=>a,x:()=>i});var t=s(6540);const n={},o=t.createContext(n);function a(e){const r=t.useContext(o);return t.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function i(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:a(e.components),t.createElement(o.Provider,{value:r},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1b5fa8ad.99194259.js b/docs/assets/js/1b5fa8ad.99194259.js
deleted file mode 100644
index aa31aa8599b..00000000000
--- a/docs/assets/js/1b5fa8ad.99194259.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9111],{5347(e,r,a){a.r(r),a.d(r,{default:()=>n});var t=a(544),l=a(4848);function n(){return(0,t.default)({sem:{id:"gads",metaTitle:"The modern alternative for NeDB",title:(0,l.jsxs)(l.Fragment,{children:["The ",(0,l.jsx)("b",{children:"modern"})," alternative for"," ",(0,l.jsx)("b",{children:"NeDB"})]}),appName:"Browser"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1c0701dd.8572a44b.js b/docs/assets/js/1c0701dd.8572a44b.js
deleted file mode 100644
index d24bf882a8a..00000000000
--- a/docs/assets/js/1c0701dd.8572a44b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6953],{8453(n,e,s){s.d(e,{R:()=>l,x:()=>a});var r=s(6540);const o={},i=r.createContext(o);function l(n){const e=r.useContext(i);return r.useMemo(function(){return"function"==typeof n?n(e):{...e,...n}},[e,n])}function a(n){let e;return e=n.disableParentContext?"function"==typeof n.components?n.components(o):n.components||o:l(n.components),r.createElement(i.Provider,{value:e},n.children)}},9328(n,e,s){s.r(e),s.d(e,{assets:()=>t,contentTitle:()=>a,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"middleware","title":"Streamlined RxDB Middleware","description":"Enhance your RxDB workflow with pre and post hooks. Quickly add custom validations, triggers, and events to streamline your asynchronous operations.","source":"@site/docs/middleware.md","sourceDirName":".","slug":"/middleware.html","permalink":"/middleware.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Streamlined RxDB Middleware","slug":"middleware.html","description":"Enhance your RxDB workflow with pre and post hooks. Quickly add custom validations, triggers, and events to streamline your asynchronous operations."},"sidebar":"tutorialSidebar","previous":{"title":"Leader Election","permalink":"/leader-election.html"},"next":{"title":"CRDT","permalink":"/crdt.html"}}');var o=s(4848),i=s(8453);const l={title:"Streamlined RxDB Middleware",slug:"middleware.html",description:"Enhance your RxDB workflow with pre and post hooks. Quickly add custom validations, triggers, and events to streamline your asynchronous operations."},a="Middleware",t={},c=[{value:"List",id:"list",level:2},{value:"Why is there no validate-hook?",id:"why-is-there-no-validate-hook",level:3},{value:"Use Cases",id:"use-cases",level:2},{value:"Usage",id:"usage",level:2},{value:"Insert",id:"insert",level:3},{value:"lifecycle",id:"lifecycle",level:4},{value:"preInsert",id:"preinsert",level:4},{value:"postInsert",id:"postinsert",level:4},{value:"Save",id:"save",level:3},{value:"lifecycle",id:"lifecycle-1",level:4},{value:"preSave",id:"presave",level:4},{value:"postSave",id:"postsave",level:4},{value:"Remove",id:"remove",level:3},{value:"lifecycle",id:"lifecycle-2",level:4},{value:"preRemove",id:"preremove",level:4},{value:"postRemove",id:"postremove",level:4},{value:"postCreate",id:"postcreate",level:3}];function d(n){const e={admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...n.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(e.header,{children:(0,o.jsx)(e.h1,{id:"middleware",children:"Middleware"})}),"\n",(0,o.jsx)(e.p,{children:"RxDB middleware-hooks (also called pre and post hooks) are functions which are passed control during execution of asynchronous functions.\nThe hooks are specified on RxCollection-level and help to create a clear what-happens-when-structure of your code."}),"\n",(0,o.jsxs)(e.p,{children:["Hooks can be defined to run ",(0,o.jsx)(e.strong,{children:"parallel"})," or as ",(0,o.jsx)(e.strong,{children:"series"})," one after another.\nHooks can be ",(0,o.jsx)(e.strong,{children:"synchronous"})," or ",(0,o.jsx)(e.strong,{children:"asynchronous"})," when they return a ",(0,o.jsx)(e.code,{children:"Promise"}),".\nTo stop the operation at a specific hook, throw an error."]}),"\n",(0,o.jsx)(e.h2,{id:"list",children:"List"}),"\n",(0,o.jsx)(e.p,{children:"RxDB supports the following hooks:"}),"\n",(0,o.jsxs)(e.ul,{children:["\n",(0,o.jsx)(e.li,{children:"preInsert"}),"\n",(0,o.jsx)(e.li,{children:"postInsert"}),"\n",(0,o.jsx)(e.li,{children:"preSave"}),"\n",(0,o.jsx)(e.li,{children:"postSave"}),"\n",(0,o.jsx)(e.li,{children:"preRemove"}),"\n",(0,o.jsx)(e.li,{children:"postRemove"}),"\n",(0,o.jsx)(e.li,{children:"postCreate"}),"\n"]}),"\n",(0,o.jsx)(e.h3,{id:"why-is-there-no-validate-hook",children:"Why is there no validate-hook?"}),"\n",(0,o.jsxs)(e.p,{children:["Different to mongoose, the validation on document-data is running on the field-level for every change to a document.\nThis means if you set the value ",(0,o.jsx)(e.code,{children:"lastName"})," of a RxDocument, then the validation will only run on the changed field, not the whole document.\nTherefore it is not useful to have validate-hooks when a document is written to the database."]}),"\n",(0,o.jsx)(e.h2,{id:"use-cases",children:"Use Cases"}),"\n",(0,o.jsx)(e.p,{children:"Middleware are useful for atomizing model logic and avoiding nested blocks of async code.\nHere are some other ideas:"}),"\n",(0,o.jsxs)(e.ul,{children:["\n",(0,o.jsx)(e.li,{children:"complex validation"}),"\n",(0,o.jsx)(e.li,{children:"removing dependent documents"}),"\n",(0,o.jsx)(e.li,{children:"asynchronous defaults"}),"\n",(0,o.jsx)(e.li,{children:"asynchronous tasks that a certain action triggers"}),"\n",(0,o.jsx)(e.li,{children:"triggering custom events"}),"\n",(0,o.jsx)(e.li,{children:"notifications"}),"\n"]}),"\n",(0,o.jsx)(e.h2,{id:"usage",children:"Usage"}),"\n",(0,o.jsxs)(e.p,{children:["All hooks have the plain data as first parameter, and all but ",(0,o.jsx)(e.code,{children:"preInsert"})," also have the ",(0,o.jsx)(e.code,{children:"RxDocument"}),"-instance as second parameter. If you want to modify the data in the hook, change attributes of the first parameter."]}),"\n",(0,o.jsxs)(e.p,{children:["All hook functions are also ",(0,o.jsx)(e.code,{children:"this"}),"-bind to the ",(0,o.jsx)(e.code,{children:"RxCollection"}),"-instance."]}),"\n",(0,o.jsx)(e.h3,{id:"insert",children:"Insert"}),"\n",(0,o.jsx)(e.p,{children:"An insert-hook receives the data-object of the new document."}),"\n",(0,o.jsx)(e.h4,{id:"lifecycle",children:"lifecycle"}),"\n",(0,o.jsxs)(e.ul,{children:["\n",(0,o.jsx)(e.li,{children:"RxCollection.insert is called"}),"\n",(0,o.jsx)(e.li,{children:"preInsert series-hooks"}),"\n",(0,o.jsx)(e.li,{children:"preInsert parallel-hooks"}),"\n",(0,o.jsx)(e.li,{children:"schema validation runs"}),"\n",(0,o.jsx)(e.li,{children:"new document is written to database"}),"\n",(0,o.jsx)(e.li,{children:"postInsert series-hooks"}),"\n",(0,o.jsx)(e.li,{children:"postInsert parallel-hooks"}),"\n",(0,o.jsx)(e.li,{children:"event is emitted to RxDatabase and RxCollection"}),"\n"]}),"\n",(0,o.jsx)(e.h4,{id:"preinsert",children:"preInsert"}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// series"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // set age to 50 before saving"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".age "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// parallel"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// async"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// stop the insert-operation"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" throw"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" Error"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'stop'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(e.h4,{id:"postinsert",children:"postInsert"}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// series"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// parallel"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// async"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(e.h3,{id:"save",children:"Save"}),"\n",(0,o.jsx)(e.p,{children:"A save-hook receives the document which is saved."}),"\n",(0,o.jsx)(e.h4,{id:"lifecycle-1",children:"lifecycle"}),"\n",(0,o.jsxs)(e.ul,{children:["\n",(0,o.jsx)(e.li,{children:"RxDocument.save is called"}),"\n",(0,o.jsx)(e.li,{children:"preSave series-hooks"}),"\n",(0,o.jsx)(e.li,{children:"preSave parallel-hooks"}),"\n",(0,o.jsx)(e.li,{children:"updated document is written to database"}),"\n",(0,o.jsx)(e.li,{children:"postSave series-hooks"}),"\n",(0,o.jsx)(e.li,{children:"postSave parallel-hooks"}),"\n",(0,o.jsx)(e.li,{children:"event is emitted to RxDatabase and RxCollection"}),"\n"]}),"\n",(0,o.jsx)(e.h4,{id:"presave",children:"preSave"}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// series"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preSave"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // modify anyField before saving"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".anyField "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'anyValue'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// parallel"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preSave"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// async"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preSave"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// stop the save-operation"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preSave"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" throw"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" Error"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'stop'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(e.h4,{id:"postsave",children:"postSave"}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// series"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postSave"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// parallel"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postSave"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// async"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postSave"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(e.h3,{id:"remove",children:"Remove"}),"\n",(0,o.jsx)(e.p,{children:"An remove-hook receives the document which is removed."}),"\n",(0,o.jsx)(e.h4,{id:"lifecycle-2",children:"lifecycle"}),"\n",(0,o.jsxs)(e.ul,{children:["\n",(0,o.jsx)(e.li,{children:"RxDocument.remove is called"}),"\n",(0,o.jsx)(e.li,{children:"preRemove series-hooks"}),"\n",(0,o.jsx)(e.li,{children:"preRemove parallel-hooks"}),"\n",(0,o.jsx)(e.li,{children:"deleted document is written to database"}),"\n",(0,o.jsx)(e.li,{children:"postRemove series-hooks"}),"\n",(0,o.jsx)(e.li,{children:"postRemove parallel-hooks"}),"\n",(0,o.jsx)(e.li,{children:"event is emitted to RxDatabase and RxCollection"}),"\n"]}),"\n",(0,o.jsx)(e.h4,{id:"preremove",children:"preRemove"}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// series"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preRemove"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// parallel"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preRemove"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// async"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preRemove"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// stop the remove-operation"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".preRemove"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" throw"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" Error"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'stop'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(e.h4,{id:"postremove",children:"postRemove"}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// series"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postRemove"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// parallel"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postRemove"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// async"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postRemove"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(res"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(e.h3,{id:"postcreate",children:"postCreate"}),"\n",(0,o.jsxs)(e.p,{children:["This hook is called whenever a ",(0,o.jsx)(e.code,{children:"RxDocument"})," is constructed.\nYou can use ",(0,o.jsx)(e.code,{children:"postCreate"})," to modify every RxDocument-instance of the collection.\nThis adds a flexible way to add specify behavior to every document. You can also use it to add custom getter/setter to documents. PostCreate-hooks cannot be ",(0,o.jsx)(e.strong,{children:"asynchronous"}),"."]}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postCreate"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(plainData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument){"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" Object"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".defineProperty"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(rxDocument"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myField'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" get"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".myField);"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'foobar'"})})]})})}),"\n",(0,o.jsx)(e.admonition,{type:"note",children:(0,o.jsxs)(e.p,{children:["This hook does not run on already created or cached documents. Make sure to add ",(0,o.jsx)(e.code,{children:"postCreate"}),"-hooks before interacting with the collection."]})})]})}function h(n={}){const{wrapper:e}={...(0,i.R)(),...n.components};return e?(0,o.jsx)(e,{...n,children:(0,o.jsx)(d,{...n})}):d(n)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1da545ff.ef424470.js b/docs/assets/js/1da545ff.ef424470.js
deleted file mode 100644
index dc146ed39f4..00000000000
--- a/docs/assets/js/1da545ff.ef424470.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9743],{135(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"releases/9.0.0","title":"RxDB 9.0.0 - Faster & Simpler","description":"Discover RxDB 9.0.0\'s streamlined plugins, top-level schema definitions, and improved dev-mode. Experience a simpler, faster real-time database.","source":"@site/docs/releases/9.0.0.md","sourceDirName":"releases","slug":"/releases/9.0.0.html","permalink":"/releases/9.0.0.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB 9.0.0 - Faster & Simpler","slug":"9.0.0.html","description":"Discover RxDB 9.0.0\'s streamlined plugins, top-level schema definitions, and improved dev-mode. Experience a simpler, faster real-time database."},"sidebar":"tutorialSidebar","previous":{"title":"10.0.0","permalink":"/releases/10.0.0.html"},"next":{"title":"8.0.0","permalink":"/releases/8.0.0.html"}}');var i=n(4848),a=n(8453);const o={title:"RxDB 9.0.0 - Faster & Simpler",slug:"9.0.0.html",description:"Discover RxDB 9.0.0's streamlined plugins, top-level schema definitions, and improved dev-mode. Experience a simpler, faster real-time database."},t="9.0.0",l={},d=[{value:"Breaking changes",id:"breaking-changes",level:2},{value:"All default exports have been removed",id:"all-default-exports-have-been-removed",level:3},{value:"Indexes are specified at the top level of the schema definition",id:"indexes-are-specified-at-the-top-level-of-the-schema-definition",level:3},{value:"Encrypted fields at the top level of the schema",id:"encrypted-fields-at-the-top-level-of-the-schema",level:3},{value:"New dev-mode plugin",id:"new-dev-mode-plugin",level:3},{value:"New migration plugin",id:"new-migration-plugin",level:3},{value:"Rewritten key-compression",id:"rewritten-key-compression",level:3},{value:"Rewritten query-change-detection to event-reduce",id:"rewritten-query-change-detection-to-event-reduce",level:3},{value:"find() and findOne() now accepts the full mango query",id:"find-and-findone-now-accepts-the-full-mango-query",level:3},{value:"moved query builder to own plugin",id:"moved-query-builder-to-own-plugin",level:3},{value:"Refactored RxChangeEvent",id:"refactored-rxchangeevent",level:3},{value:"Internal hash() is now using a salt",id:"internal-hash-is-now-using-a-salt",level:3},{value:"Changed default of RxDocument.toJSON()",id:"changed-default-of-rxdocumenttojson",level:3},{value:"Typescript 3.8.0 or newer is required",id:"typescript-380-or-newer-is-required",level:3},{value:"GraphQL replication will run a schema validation of incoming data",id:"graphql-replication-will-run-a-schema-validation-of-incoming-data",level:3},{value:"Internal and other changes",id:"internal-and-other-changes",level:2},{value:"Help wanted",id:"help-wanted",level:2},{value:"Refactor data-migrator",id:"refactor-data-migrator",level:3},{value:"Add e2e tests to the react example",id:"add-e2e-tests-to-the-react-example",level:3},{value:"Fix pouchdb bug so we can upgrade pouchdb-find",id:"fix-pouchdb-bug-so-we-can-upgrade-pouchdb-find",level:3},{value:"About the future of RxDB",id:"about-the-future-of-rxdb",level:2}];function c(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"900",children:"9.0.0"})}),"\n",(0,i.jsx)(s.p,{children:"So I was working hard the past month to prepare everything for the next major release of RxDB. The last major release was 1,5 years ago by the way."}),"\n",(0,i.jsxs)(s.p,{children:["When I started ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/issues/1636",children:"listing up the planned changes"})," I had big ambitions about basically rewriting everything. But I found out this time has not come yet. There is some work to be done first. So version ",(0,i.jsx)(s.code,{children:"9.0.0"})," was more about fixing all these small things that made improving the codebase difficult. Much has been refactored and moved. Some API-parts have been changed to have a more simple project with a cleaner codebase."]}),"\n",(0,i.jsx)(s.p,{children:"Notice that I use major releases to bundle stuff that breaks the RxDB usage in your project. By having only few major releases you can be sure that you can upgrade in one big block instead of changing stuff each few months. Big features are released in non-major releases because they mostly can be implemented without side effects."}),"\n",(0,i.jsx)(s.h2,{id:"breaking-changes",children:"Breaking changes"}),"\n",(0,i.jsx)(s.p,{children:"You have to apply these changes to your codebase when upgrading RxDB."}),"\n",(0,i.jsx)(s.h3,{id:"all-default-exports-have-been-removed",children:"All default exports have been removed"}),"\n",(0,i.jsx)(s.p,{children:"Using default exports and imports can be helpful when you want to write code fast.\nBut using them also disabled the tree-shaking of your bundler which means you added much code to your bundle that was not even used. To prevent this common behavior, I removed all default exports and renamed functions so that they are more unlikely to clash with other non-RxDB function names."}),"\n",(0,i.jsx)(s.p,{children:"Instead of doing"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" RxDB "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".plugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" RxDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".create"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]})]})})}),"\n",(0,i.jsx)(s.p,{children:"You now do"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" addRxPlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Also ",(0,i.jsx)(s.code,{children:"removeDatabase()"})," is renamed to ",(0,i.jsx)(s.code,{children:"removeRxDatabase()"})," and ",(0,i.jsx)(s.code,{children:"plugin()"})," is now ",(0,i.jsx)(s.code,{children:"addRxPlugin()"}),"."]}),"\n",(0,i.jsx)(s.p,{children:"Same goes for all previous default exports of the plugins."}),"\n",(0,i.jsx)(s.h3,{id:"indexes-are-specified-at-the-top-level-of-the-schema-definition",children:"Indexes are specified at the top level of the schema definition"}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/issues/1655",children:"related issue"})}),"\n",(0,i.jsx)(s.p,{children:"In the past the indexes of a collection had to be specified at the field level of the schema like"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "firstName"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "index"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"This made it complex to list up the index fields which had a bad performance on startup.\nTo fix this the indexes are now specified at the top level of the schema like"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "title"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "my schema"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "version"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "object"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "properties"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "indexes"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "firstName"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"compound"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "index"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"encrypted-fields-at-the-top-level-of-the-schema",children:"Encrypted fields at the top level of the schema"}),"\n",(0,i.jsx)(s.p,{children:"Same as the indexes, encrypted fields are now also defined in the top level like"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "title"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "my schema"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "version"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "object"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "properties"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "encrypted"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "password"'})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"new-dev-mode-plugin",children:"New dev-mode plugin"}),"\n",(0,i.jsxs)(s.p,{children:["In the past we had stuff that is only wanted for development in the two plugins ",(0,i.jsx)(s.code,{children:"error-messages"})," and ",(0,i.jsx)(s.code,{children:"schema-check"}),"."]}),"\n",(0,i.jsxs)(s.p,{children:["Now we have a single plugin ",(0,i.jsx)(s.code,{children:"dev-mode"})," that contains all these checks and development helper functions. I also moved many other checks out of the core-module into dev-mode."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBDevModePlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/dev-mode'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBDevModePlugin);"})]})]})})}),"\n",(0,i.jsx)(s.h3,{id:"new-migration-plugin",children:"New migration plugin"}),"\n",(0,i.jsx)(s.p,{children:"The data migration was not used by all users of this project. Often it was easier to just wipe the local data store and let the client sync everything from the server. Because the migration has so much code, it is now in a separate plugin so that you do not have to ship this code to the clients if not necessary."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBMigrationPlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/migration'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBMigrationPlugin);"})]})]})})}),"\n",(0,i.jsx)(s.h3,{id:"rewritten-key-compression",children:"Rewritten key-compression"}),"\n",(0,i.jsx)(s.p,{children:"The key-compression logic was fully coded only for RxDB. This was a problem because it was not usable for other stuff and also badly tested. We had a known problem with nested arrays that caused much confusion because some queries did not find the correct documents."}),"\n",(0,i.jsxs)(s.p,{children:["I now created a npm-package ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/jsonschema-key-compression",children:"jsonschema-key-compression"})," that has cleaner code, better tests and can also be used for non-RxDB stuff."]}),"\n",(0,i.jsx)(s.p,{children:"If you used the key-compression in the past and have clients out there with old data, you have to find a way to migrate that data by using the json-import or other solutions depending on your project."}),"\n",(0,i.jsx)(s.h3,{id:"rewritten-query-change-detection-to-event-reduce",children:"Rewritten query-change-detection to event-reduce"}),"\n",(0,i.jsxs)(s.p,{children:["One big benefit of having a ",(0,i.jsx)(s.a,{href:"/articles/realtime-database.html",children:"realtime database"})," is that big performance optimizations can be done when the database knows a query is observed and the updated results are needed continuously. In the past this optimization was done by the internal ",(0,i.jsx)(s.code,{children:"queryChangeDetection"})," which was a big tree of if-else-statements that hopefully worked. This was also the reason why queryChangeDetection was in beta mode and not enabled by default."]}),"\n",(0,i.jsxs)(s.p,{children:["After months of research and testing I was able to create ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/event-reduce",children:"Event-Reduce: An algorithm to optimize database queries that run multiple times"}),". This JavaScript module contains an algorithm that is able to optimize realtime queries in a way that was not possible before. The algorithm is not RxDB specific and also heavily tested."]}),"\n",(0,i.jsxs)(s.p,{children:["Instead of setting ",(0,i.jsx)(s.code,{children:"queryChangeDetection"})," when creating a ",(0,i.jsx)(s.code,{children:"RxDatabase"}),", you now set ",(0,i.jsx)(s.code,{children:"eventReduce"})," which defaults to ",(0,i.jsx)(s.code,{children:"true"}),"."]}),"\n",(0,i.jsx)(s.h3,{id:"find-and-findone-now-accepts-the-full-mango-query",children:"find() and findOne() now accepts the full mango query"}),"\n",(0,i.jsxs)(s.p,{children:["In the past, only the selector of a query could be passed to ",(0,i.jsx)(s.code,{children:"find()"})," and ",(0,i.jsx)(s.code,{children:"findOne()"})," if you wanted to also do ",(0,i.jsx)(s.code,{children:"sort"}),", ",(0,i.jsx)(s.code,{children:"skip"})," or ",(0,i.jsx)(s.code,{children:"limit"}),", you had to call additional functions like"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".skip"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"5"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".limit"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,i.jsx)(s.p,{children:"Now you can pass the full query to the function call like"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" skip"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" limit"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"moved-query-builder-to-own-plugin",children:"moved query builder to own plugin"}),"\n",(0,i.jsxs)(s.p,{children:["The query builder that allowed to create queries like ",(0,i.jsx)(s.code,{children:".where('foo').eq('bar')"})," etc. was not really used by many people. Most of the time it is better to just pass the full query as simple json object. Also the code for the query builder was big and increased the build size much more than its value added. Only some edge-cases where recursive query modification was needed made the query builder useful. If you still want to use the query builder, you have to import the plugin."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBQueryBuilderPlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/query-builder'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBQueryBuilderPlugin);"})]})]})})}),"\n",(0,i.jsx)(s.h3,{id:"refactored-rxchangeevent",children:"Refactored RxChangeEvent"}),"\n",(0,i.jsxs)(s.p,{children:["The whole data structure of ",(0,i.jsx)(s.code,{children:"RxChangeEvent"})," was way more complicated than it had to be. I refactored the whole class to be more simple. If you directly use ",(0,i.jsx)(s.code,{children:"RxChangeEvent"})," in your project you have to adapt to these changes. Also the stream of ",(0,i.jsx)(s.code,{children:"RxDatabase().$"})," will no longer emit the ",(0,i.jsx)(s.code,{children:"COLLECTION"})," event when a new collection is created."]}),"\n",(0,i.jsx)(s.h3,{id:"internal-hash-is-now-using-a-salt",children:"Internal hash() is now using a salt"}),"\n",(0,i.jsxs)(s.p,{children:["The internal hash function was used to store hashes of database passwords to compare them and directly throw errors when the wrong password was used with an existing data set. This was dangerous because you could use rainbow tables or even ",(0,i.jsx)(s.a,{href:"https://www.google.com/search?q=e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",children:"just google"})," the hash to find out the plain password. So now the internal hashing is using a salt to prevent these attacks. If you have used the encryption in the past, you have to migrate your internal schema storage."]}),"\n",(0,i.jsx)(s.h3,{id:"changed-default-of-rxdocumenttojson",children:"Changed default of RxDocument.toJSON()"}),"\n",(0,i.jsxs)(s.p,{children:["By default ",(0,i.jsx)(s.code,{children:"RxDocument.toJSON()"})," always returned also the ",(0,i.jsx)(s.code,{children:"_rev"})," field and the ",(0,i.jsx)(s.code,{children:"_attachments"}),". This was confusing behavior which is why I changed the default to ",(0,i.jsx)(s.code,{children:"RxDocument().toJSON(withRevAndAttachments = false)"})]}),"\n",(0,i.jsx)(s.h3,{id:"typescript-380-or-newer-is-required",children:"Typescript 3.8.0 or newer is required"}),"\n",(0,i.jsxs)(s.p,{children:["Because RxDB and some subdependencies extensively use ",(0,i.jsx)(s.code,{children:"export type ..."})," you now need typescript ",(0,i.jsx)(s.code,{children:"3.8.0"})," or newer."]}),"\n",(0,i.jsx)(s.h3,{id:"graphql-replication-will-run-a-schema-validation-of-incoming-data",children:"GraphQL replication will run a schema validation of incoming data"}),"\n",(0,i.jsx)(s.p,{children:"In dev-mode, the GraphQL-replication will run a schema validation of each document that comes from the server before it is saved to the database."}),"\n",(0,i.jsx)(s.h2,{id:"internal-and-other-changes",children:"Internal and other changes"}),"\n",(0,i.jsx)(s.p,{children:"I refactored much internal stuff and moved much code out of the core into the specific plugins."}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Renamed ",(0,i.jsx)(s.code,{children:"RxSchema.jsonID"})," to ",(0,i.jsx)(s.code,{children:"RxSchema.jsonSchema"})]}),"\n",(0,i.jsx)(s.li,{children:"Moved remaining stuff of leader-election from core into the plugin"}),"\n",(0,i.jsxs)(s.li,{children:["Merged multiple internal databases for metadata into one ",(0,i.jsx)(s.code,{children:"internalStore"})]}),"\n",(0,i.jsx)(s.li,{children:"Removed many runtime type checks that now should be covered by typescript in buildtime"}),"\n",(0,i.jsx)(s.li,{children:"The GraphQL replication is now out of beta mode"}),"\n",(0,i.jsxs)(s.li,{children:["Removed documentation examples for ",(0,i.jsx)(s.code,{children:"require()"})," CommonJS loading"]}),"\n",(0,i.jsxs)(s.li,{children:["Removed ",(0,i.jsx)(s.code,{children:"RxCollection.docChanges$()"})," because all events are from the docs"]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"help-wanted",children:"Help wanted"}),"\n",(0,i.jsx)(s.p,{children:"RxDB is an open source project an heavily relies on the contribution of its users. There are some things that must be done, but I have no time for them."}),"\n",(0,i.jsx)(s.h3,{id:"refactor-data-migrator",children:"Refactor data-migrator"}),"\n",(0,i.jsx)(s.p,{children:"The current implementation has some flaws and should be completely rewritten."}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"It does not use pouchdb's bulkDocs which is much faster"}),"\n",(0,i.jsx)(s.li,{children:"It could have been written without rxjs and with less code that is easier to understand"}),"\n",(0,i.jsx)(s.li,{children:"It does not migrate the revisions of documents which causes a problem when replication is used"}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"add-e2e-tests-to-the-react-example",children:"Add e2e tests to the react example"}),"\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/react",children:"react example"})," has no end-to-end tests which is why the CI does not ensure that it works all the time. We should add some basic tests like we did for the other example projects."]}),"\n",(0,i.jsx)(s.h3,{id:"fix-pouchdb-bug-so-we-can-upgrade-pouchdb-find",children:"Fix pouchdb bug so we can upgrade pouchdb-find"}),"\n",(0,i.jsxs)(s.p,{children:["There is a ",(0,i.jsx)(s.a,{href:"https://github.com/pouchdb/pouchdb/issues/7810",children:"bug in pouchdb"})," that prevents the upgrade of ",(0,i.jsx)(s.code,{children:"pouchdb-find"}),". This is why RxDB relies on an old version of ",(0,i.jsx)(s.code,{children:"pouchdb-find"})," that also requires different sub-dependencies. This increases the build size a lot because for example we ship multiple version of ",(0,i.jsx)(s.code,{children:"spark-md5"})," and others."]}),"\n",(0,i.jsx)(s.h2,{id:"about-the-future-of-rxdb",children:"About the future of RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["At the moment RxDB is a realtime database based on pouchdb. In the future I want RxDB to be a wrapper around pull-based databases that also works with other source-dbs like mongoDB or PostgreSQL. As a start I defined the ",(0,i.jsx)(s.code,{children:"RxStorage"})," interface and created a ",(0,i.jsx)(s.code,{children:"RxStoragePouchdb"})," class that implements it and contains all pouchdb-specific logic. I want to move every direct storage usage into that interface so that later we can create other implementations of it for other source databases."]})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}},8453(e,s,n){n.d(s,{R:()=>o,x:()=>t});var r=n(6540);const i={},a=r.createContext(i);function o(e){const s=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),r.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1db64337.7448d5c4.js b/docs/assets/js/1db64337.7448d5c4.js
deleted file mode 100644
index 25d264f5d65..00000000000
--- a/docs/assets/js/1db64337.7448d5c4.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8413],{6471(e,t,a){a.r(t),a.d(t,{assets:()=>m,contentTitle:()=>y,default:()=>x,frontMatter:()=>b,metadata:()=>r,toc:()=>g});const r=JSON.parse('{"id":"overview","title":"RxDB Docs","description":"RxDB Documentation Overview","source":"@site/docs/overview.md","sourceDirName":".","slug":"/overview.html","permalink":"/overview.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB Docs","slug":"overview.html","description":"RxDB Documentation Overview"},"sidebar":"tutorialSidebar","next":{"title":"\ud83d\ude80 Quickstart","permalink":"/quickstart.html"}}');var l=a(4848),i=a(8453),o=a(7810);const c={tutorialSidebar:[{type:"category",label:"Getting Started",collapsed:!1,items:[{type:"doc",id:"overview",label:"Overview"},"quickstart","install","dev-mode","tutorials/typescript"]},{type:"category",label:"Core Entities",collapsed:!0,items:[{type:"doc",id:"rx-database",label:"RxDatabase"},{type:"doc",id:"rx-schema",label:"RxSchema"},{type:"doc",id:"rx-collection",label:"RxCollection"},{type:"doc",id:"rx-document",label:"RxDocument"},{type:"doc",id:"rx-query",label:"RxQuery"}]},{type:"category",label:"\ud83d\udcbe Storages",items:[{type:"doc",id:"rx-storage",label:"RxStorage Overview"},{type:"doc",id:"rx-storage-localstorage",label:"LocalStorage (Browser)"},{type:"doc",id:"rx-storage-indexeddb",label:"IndexedDB \ud83d\udc51 (Browser, Capacitor)"},{type:"doc",id:"rx-storage-opfs",label:"OPFS \ud83d\udc51 (Browser)"},{type:"doc",id:"rx-storage-memory",label:"Memory"},{type:"doc",id:"rx-storage-filesystem-node",label:"Filesystem Node \ud83d\udc51 (Node.js)"},{type:"doc",id:"rx-storage-sqlite",label:"SQLite (Capacitor, React-Native, Expo, Tauri, Electron, Node.js)"},{type:"category",label:"Third Party Storages",items:[{type:"doc",id:"rx-storage-dexie",label:"Dexie.js"},{type:"doc",id:"rx-storage-mongodb",label:"MongoDB"},{type:"doc",id:"rx-storage-denokv",label:"DenoKV"},{type:"doc",id:"rx-storage-foundationdb",label:"FoundationDB"}]}]},{type:"category",label:"Storage Wrappers",items:[{type:"doc",id:"schema-validation",label:"Schema Validation"},{type:"doc",id:"encryption",label:"Encryption"},{type:"doc",id:"key-compression",label:"Key Compression"},{type:"doc",id:"logger",label:"Logger \ud83d\udc51"},{type:"doc",id:"rx-storage-remote",label:"Remote RxStorage"},{type:"doc",id:"rx-storage-worker",label:"Worker RxStorage \ud83d\udc51"},{type:"doc",id:"rx-storage-shared-worker",label:"SharedWorker RxStorage \ud83d\udc51"},{type:"doc",id:"rx-storage-memory-mapped",label:"Memory Mapped RxStorage \ud83d\udc51"},{type:"doc",id:"rx-storage-sharding",label:"Sharding \ud83d\udc51"},{type:"doc",id:"rx-storage-localstorage-meta-optimizer",label:"Localstorage Meta Optimizer \ud83d\udc51"},{type:"doc",id:"electron",label:"Electron"}]},{type:"category",label:"\ud83d\udd04 Replication",items:[{type:"doc",id:"replication",label:"\u2699\ufe0f Sync Engine"},{type:"doc",id:"replication-http",label:"HTTP Replication"},{type:"doc",id:"replication-server",label:"RxServer Replication"},{type:"doc",id:"replication-graphql",label:"GraphQL Replication"},{type:"doc",id:"replication-websocket",label:"WebSocket Replication"},{type:"doc",id:"replication-couchdb",label:"CouchDB Replication"},{type:"doc",id:"replication-webrtc",label:"WebRTC P2P Replication"},{type:"doc",id:"replication-firestore",label:"Firestore Replication"},{type:"doc",id:"replication-mongodb",label:"MongoDB Replication"},{type:"doc",id:"replication-supabase",label:"Supabase Replication"},{type:"doc",id:"replication-nats",label:"NATS Replication"},{type:"doc",id:"replication-appwrite",label:"Appwrite Replication"}]},{type:"category",label:"Server",items:[{type:"doc",id:"rx-server",label:"RxServer"},{type:"doc",id:"rx-server-scaling",label:"RxServer Scaling"}]},{type:"category",label:"How RxDB works",items:[{type:"doc",id:"transactions-conflicts-revisions",label:"Transactions Conflicts Revisions"},{type:"doc",id:"query-cache",label:"Query Cache"},{type:"doc",id:"plugins",label:"Creating Plugins"},{type:"doc",id:"errors",label:"Errors"}]},{type:"category",label:"Advanced Features",items:[{type:"category",label:"Migration",items:[{type:"doc",id:"migration-schema",label:"Schema Migration"},{type:"doc",id:"migration-storage",label:"Storage Migration"}]},{type:"doc",id:"rx-attachment",label:"Attachments"},{type:"doc",id:"rx-pipeline",label:"RxPipelines"},{type:"doc",id:"reactivity",label:"Custom Reactivity"},{type:"doc",id:"rx-state",label:"RxState"},{type:"doc",id:"rx-local-document",label:"Local Documents"},{type:"doc",id:"cleanup",label:"Cleanup"},{type:"doc",id:"backup",label:"Backup"},{type:"doc",id:"leader-election",label:"Leader Election"},{type:"doc",id:"middleware",label:"Middleware"},{type:"doc",id:"crdt",label:"CRDT"},{type:"doc",id:"population",label:"Population"},{type:"doc",id:"orm",label:"ORM"},{type:"doc",id:"fulltext-search",label:"Fulltext Search \ud83d\udc51"},{type:"doc",id:"articles/javascript-vector-database",label:"Vector Database"},{type:"doc",id:"query-optimizer",label:"Query Optimizer \ud83d\udc51"},{type:"doc",id:"third-party-plugins",label:"Third Party Plugins"}]},{type:"category",label:"Integrations",items:[{type:"doc",id:"react",label:"React"},{type:"link",label:"TanStack DB",href:"https://tanstack.com/db/latest/docs/collections/rxdb-collection",customProps:{target:"_blank"}}]},{type:"category",label:"Performance",items:[{type:"doc",id:"rx-storage-performance",label:"RxStorage Performance"},{type:"doc",id:"nosql-performance-tips",label:"NoSQL Performance Tips"},{type:"doc",id:"slow-indexeddb",label:"Slow IndexedDB"},{type:"doc",id:"articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm",label:"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite"}]},{type:"category",label:"Releases",items:[{type:"doc",id:"releases/17.0.0",label:"17.0.0"},{type:"doc",id:"releases/16.0.0",label:"16.0.0"},{type:"doc",id:"releases/15.0.0",label:"15.0.0"},{type:"doc",id:"releases/14.0.0",label:"14.0.0"},{type:"doc",id:"releases/13.0.0",label:"13.0.0"},{type:"doc",id:"releases/12.0.0",label:"12.0.0"},{type:"doc",id:"releases/11.0.0",label:"11.0.0"},{type:"doc",id:"releases/10.0.0",label:"10.0.0"},{type:"doc",id:"releases/9.0.0",label:"9.0.0"},{type:"doc",id:"releases/8.0.0",label:"8.0.0"}]},{type:"category",label:"Articles",items:["articles/browser-database","articles/local-first-future","why-nosql","downsides-of-offline-first","nodejs-database","offline-first","react-native-database","articles/angular-database","articles/browser-storage","articles/data-base","articles/embedded-database","articles/flutter-database","articles/frontend-database","articles/in-memory-nosql-database","articles/ionic-database","articles/ionic-storage","articles/json-database","articles/websockets-sse-polling-webrtc-webtransport","articles/localstorage","articles/mobile-database","articles/progressive-web-app-database","articles/react-database","articles/realtime-database","articles/angular-indexeddb","articles/react-indexeddb","capacitor-database","alternatives","electron-database","articles/optimistic-ui","articles/local-database","articles/react-native-encryption","articles/vue-database","articles/vue-indexeddb","articles/jquery-database","articles/firestore-alternative","articles/firebase-realtime-database-alternative","articles/offline-database","articles/zero-latency-local-first","articles/indexeddb-max-storage-limit","articles/json-based-database","articles/reactjs-storage"]},"contribute",{type:"category",label:"Contact",items:[{type:"link",label:"Consulting",href:"/consulting/"},{type:"link",label:"Discord",href:"/chat/",customProps:{target:"_blank"}},{type:"link",label:"LinkedIn",href:"https://www.linkedin.com/company/rxdb/"}]}]};var s=a(8342);function d(e){const t=(0,l.jsx)("div",{className:"premium-block hover-shadow-middle bg-gradient-right-top",children:(0,l.jsxs)("div",{className:"premium-block-inner",children:[(0,l.jsx)("h4",{style:{textDecoration:"none"},children:(0,s.Z2)(e.title)}),(0,l.jsx)("p",{children:e.text})]})});return e.href?(0,l.jsx)("a",{href:e.href,target:e.target,style:{textDecoration:"none"},children:t}):t}function n(){return(0,l.jsx)(l.Fragment,{children:c.tutorialSidebar.map(e=>{if("category"===e.type&&"articles"!==e.label.toLowerCase()){const t=e.type+"--"+e.label;return(0,l.jsxs)("div",{children:[(0,l.jsx)("br",{}),(0,l.jsx)("br",{}),(0,l.jsx)("h2",{style:{width:"100%"},children:e.label}),(0,l.jsx)("div",{className:"premium-blocks",children:e.items.map(e=>{if("string"==typeof e)return(0,l.jsx)(d,{title:p(e),href:"./"+e+".html"},e);if("category"===e.type)return e.items.map(e=>(0,l.jsx)(d,{title:p(e.label),href:"link"===e.type?e.href:"./"+e.id+".html",target:"link"===e.type?"_blank":void 0},e.id));{const t="link"===e.type?e.label:e.id;return(0,l.jsx)(d,{title:p(e.label),href:"link"===e.type?e.href:"./"+e.id+".html",target:"link"===e.type?"_blank":void 0},t)}})},e.label)]},t)}})})}function p(e){const t=e.split("/");return(0,o.dG)(t)}const b={title:"RxDB Docs",slug:"overview.html",description:"RxDB Documentation Overview"},y="RxDB Documentation",m={},g=[];function u(e){const t={h1:"h1",header:"header",...(0,i.R)(),...e.components};return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(t.header,{children:(0,l.jsx)(t.h1,{id:"rxdb-documentation",children:"RxDB Documentation"})}),"\n",(0,l.jsx)(n,{})]})}function x(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,l.jsx)(t,{...e,children:(0,l.jsx)(u,{...e})}):u(e)}},8342(e,t,a){a.d(t,{L$:()=>o,Z2:()=>i,zs:()=>l});var r="abcdefghijklmnopqrstuvwxyz";function l(e=10){for(var t="",a=0;ao,x:()=>c});var r=a(6540);const l={},i=r.createContext(l);function o(e){const t=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(l):e.components||l:o(e.components),r.createElement(i.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1e0353aa.bd262216.js b/docs/assets/js/1e0353aa.bd262216.js
deleted file mode 100644
index c6316ac1149..00000000000
--- a/docs/assets/js/1e0353aa.bd262216.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8588],{2219(e,r,s){s.r(r),s.d(r,{assets:()=>l,contentTitle:()=>t,default:()=>c,frontMatter:()=>i,metadata:()=>o,toc:()=>d});const o=JSON.parse('{"id":"rx-storage","title":"\u2699\ufe0f RxStorage Layer - Choose the Perfect RxDB Storage for Every Use Case","description":"Discover how RxDB\'s modular RxStorage lets you swap engines and unlock top performance, no matter the environment or use case.","source":"@site/docs/rx-storage.md","sourceDirName":".","slug":"/rx-storage.html","permalink":"/rx-storage.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"\u2699\ufe0f RxStorage Layer - Choose the Perfect RxDB Storage for Every Use Case","slug":"rx-storage.html","description":"Discover how RxDB\'s modular RxStorage lets you swap engines and unlock top performance, no matter the environment or use case."},"sidebar":"tutorialSidebar","previous":{"title":"RxQuery","permalink":"/rx-query.html"},"next":{"title":"LocalStorage (Browser)","permalink":"/rx-storage-localstorage.html"}}');var a=s(4848),n=s(8453);const i={title:"\u2699\ufe0f RxStorage Layer - Choose the Perfect RxDB Storage for Every Use Case",slug:"rx-storage.html",description:"Discover how RxDB's modular RxStorage lets you swap engines and unlock top performance, no matter the environment or use case."},t="RxStorage",l={},d=[{value:"Quick Recommendations",id:"quick-recommendations",level:2},{value:"Configuration Examples",id:"configuration-examples",level:2},{value:"Storing much data in a browser securely",id:"storing-much-data-in-a-browser-securely",level:3},{value:"High query Load",id:"high-query-load",level:3},{value:"Low Latency on Writes and Simple Reads",id:"low-latency-on-writes-and-simple-reads",level:3},{value:"All RxStorage Implementations List",id:"all-rxstorage-implementations-list",level:2},{value:"Memory",id:"memory",level:3},{value:"LocalStorage",id:"localstorage",level:3},{value:"\ud83d\udc51 IndexedDB",id:"-indexeddb",level:3},{value:"\ud83d\udc51 OPFS",id:"-opfs",level:3},{value:"\ud83d\udc51 Filesystem Node",id:"-filesystem-node",level:3},{value:"Storage Wrapper Plugins",id:"storage-wrapper-plugins",level:3},{value:"\ud83d\udc51 Worker",id:"-worker",level:4},{value:"\ud83d\udc51 SharedWorker",id:"-sharedworker",level:4},{value:"Remote",id:"remote",level:4},{value:"\ud83d\udc51 Sharding",id:"-sharding",level:4},{value:"\ud83d\udc51 Memory Mapped",id:"-memory-mapped",level:4},{value:"\ud83d\udc51 Localstorage Meta Optimizer",id:"-localstorage-meta-optimizer",level:4},{value:"Electron IpcRenderer & IpcMain",id:"electron-ipcrenderer--ipcmain",level:4},{value:"Third Party based Storages",id:"third-party-based-storages",level:3},{value:"\ud83d\udc51 SQLite",id:"-sqlite",level:4},{value:"Dexie.js",id:"dexiejs",level:4},{value:"MongoDB",id:"mongodb",level:4},{value:"DenoKV",id:"denokv",level:4},{value:"FoundationDB",id:"foundationdb",level:4}];function h(e){const r={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,n.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(r.header,{children:(0,a.jsx)(r.h1,{id:"rxstorage",children:"RxStorage"})}),"\n",(0,a.jsxs)(r.p,{children:["RxDB is not a self contained database. Instead the data is stored in an implementation of the ",(0,a.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/blob/master/src/types/rx-storage.interface.d.ts",children:"RxStorage interface"}),". This allows you to ",(0,a.jsx)(r.strong,{children:"switch out"})," the underlying data layer, depending on the JavaScript environment and performance requirements. For example you can use the SQLite storage for a capacitor app or you can use the LocalStorage RxStorage to store data in localstorage in a browser based application. There are also storages for other JavaScript runtimes like Node.js, React-Native, NativeScript and more."]}),"\n",(0,a.jsx)(r.h2,{id:"quick-recommendations",children:"Quick Recommendations"}),"\n",(0,a.jsxs)(r.ul,{children:["\n",(0,a.jsxs)(r.li,{children:["In the Browser: Use the ",(0,a.jsx)(r.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage"})," storage for simple setup and small build size. For bigger datasets, use either the ",(0,a.jsx)(r.a,{href:"/rx-storage-dexie.html",children:"dexie.js storage"})," (free) or the ",(0,a.jsx)(r.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"})," if you have ",(0,a.jsx)(r.a,{href:"/premium/",children:"\ud83d\udc51 premium access"})," which is a bit faster and has a smaller build size."]}),"\n",(0,a.jsxs)(r.li,{children:["In ",(0,a.jsx)(r.a,{href:"/electron-database.html",children:"Electron"})," and ",(0,a.jsx)(r.a,{href:"/react-native-database.html",children:"ReactNative"}),": Use the ",(0,a.jsx)(r.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," if you have ",(0,a.jsx)(r.a,{href:"/premium/",children:"\ud83d\udc51 premium access"})," or the ",(0,a.jsx)(r.a,{href:"/rx-storage-sqlite.html",children:"trial-SQLite RxStorage"})," for tryouts."]}),"\n",(0,a.jsxs)(r.li,{children:["In Capacitor: Use the ",(0,a.jsx)(r.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," if you have ",(0,a.jsx)(r.a,{href:"/premium/",children:"\ud83d\udc51 premium access"}),", otherwise use the ",(0,a.jsx)(r.a,{href:"/rx-storage-localstorage.html",children:"localStorage"})," storage."]}),"\n"]}),"\n",(0,a.jsx)(r.h2,{id:"configuration-examples",children:"Configuration Examples"}),"\n",(0,a.jsx)(r.p,{children:"The RxStorage layer of RxDB is very flexible. Here are some examples on how to configure more complex settings:"}),"\n",(0,a.jsx)(r.h3,{id:"storing-much-data-in-a-browser-securely",children:"Storing much data in a browser securely"}),"\n",(0,a.jsx)(r.p,{children:"Lets say you build a browser app that needs to store a big amount of data as secure as possible. Here we can use a combination of the storages (encryption, IndexedDB, compression, schema-checks) that increase security and reduce the stored data size."}),"\n",(0,a.jsxs)(r.p,{children:["We use the schema-validation on the top level to ensure schema-errors are clearly readable and do not contain ",(0,a.jsx)(r.a,{href:"/encryption.html",children:"encrypted"}),"/",(0,a.jsx)(r.a,{href:"/key-compression.html",children:"compressed"})," data. The encryption is used inside of the compression because encryption of compressed data is more efficient."]}),"\n",(0,a.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedValidateAjvStorage } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/validate-ajv'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedKeyCompressionStorage } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/key-compression'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedKeyEncryptionCryptoJsStorage } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/encryption-crypto-js'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedValidateAjvStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyCompressionStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyEncryptionCryptoJsStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(r.h3,{id:"high-query-load",children:"High query Load"}),"\n",(0,a.jsxs)(r.p,{children:["Also we can utilize a combination of storages to create a database that is optimized to run complex queries on the data really fast. Here we use the sharding storage together with the worker storage. This allows to run queries in parallel multithreading instead of a single JavaScript process. Because the worker initialization can slow down the initial page load, we also use the ",(0,a.jsx)(r.a,{href:"/rx-storage-localstorage-meta-optimizer.html",children:"localstorage-meta-optimizer"})," to improve initialization time."]}),"\n",(0,a.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageSharding } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sharding'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageWorker } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getLocalstorageMetaOptimizerRxStorage } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-localstorage-meta-optimizer'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getLocalstorageMetaOptimizerRxStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSharding"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageWorker"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'path/to/worker.js'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(r.h3,{id:"low-latency-on-writes-and-simple-reads",children:"Low Latency on Writes and Simple Reads"}),"\n",(0,a.jsx)(r.p,{children:"Here we create a storage configuration that is optimized to have a low latency on simple reads and writes. It uses the memory-mapped storage to fetch and store data in memory. For persistence the OPFS storage is used in the main thread which has lower latency for fetching big chunks of data when at initialization the data is loaded from disc into memory. We do not use workers because sending data from the main thread to workers and backwards would increase the latency."}),"\n",(0,a.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getLocalstorageMetaOptimizerRxStorage } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-localstorage-meta-optimizer'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getMemoryMappedRxStorage } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-memory-mapped'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageOPFSMainThread } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getLocalstorageMetaOptimizerRxStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getMemoryMappedRxStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageOPFSMainThread"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(r.h2,{id:"all-rxstorage-implementations-list",children:"All RxStorage Implementations List"}),"\n",(0,a.jsx)(r.h3,{id:"memory",children:"Memory"}),"\n",(0,a.jsxs)(r.p,{children:["A storage that stores the data in as plain data in the memory of the JavaScript process. Really fast and can be used in all environments. ",(0,a.jsx)(r.a,{href:"/rx-storage-memory.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h3,{id:"localstorage",children:"LocalStorage"}),"\n",(0,a.jsxs)(r.p,{children:["The localstroage based storage stores the data inside of a browsers ",(0,a.jsx)(r.a,{href:"/articles/localstorage.html",children:"localStorage API"}),". It is the easiest to set up and has a small bundle size. ",(0,a.jsx)(r.strong,{children:"If you are new to RxDB, you should start with the LocalStorage RxStorage"}),". ",(0,a.jsx)(r.a,{href:"/rx-storage-localstorage.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h3,{id:"-indexeddb",children:"\ud83d\udc51 IndexedDB"}),"\n",(0,a.jsxs)(r.p,{children:["The IndexedDB ",(0,a.jsx)(r.code,{children:"RxStorage"})," is based on plain IndexedDB. For most use cases, this has the best performance together with the OPFS storage. ",(0,a.jsx)(r.a,{href:"/rx-storage-indexeddb.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h3,{id:"-opfs",children:"\ud83d\udc51 OPFS"}),"\n",(0,a.jsxs)(r.p,{children:["The OPFS ",(0,a.jsx)(r.code,{children:"RxStorage"})," is based on the File System Access API. This has the best performance of all other non-in-memory storage, when RxDB is used inside of a browser. ",(0,a.jsx)(r.a,{href:"/rx-storage-opfs.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h3,{id:"-filesystem-node",children:"\ud83d\udc51 Filesystem Node"}),"\n",(0,a.jsxs)(r.p,{children:["The Filesystem Node storage is best suited when you use RxDB in a Node.js process or with ",(0,a.jsx)(r.a,{href:"/electron.html",children:"electron.js"}),". ",(0,a.jsx)(r.a,{href:"/rx-storage-filesystem-node.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h3,{id:"storage-wrapper-plugins",children:"Storage Wrapper Plugins"}),"\n",(0,a.jsx)(r.h4,{id:"-worker",children:"\ud83d\udc51 Worker"}),"\n",(0,a.jsxs)(r.p,{children:["The worker RxStorage is a wrapper around any other RxStorage which allows to run the storage in a WebWorker (in browsers) or a Worker Thread (in Node.js). By doing so, you can take CPU load from the main process and move it into the worker's process which can improve the perceived performance of your application. ",(0,a.jsx)(r.a,{href:"/rx-storage-worker.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"-sharedworker",children:"\ud83d\udc51 SharedWorker"}),"\n",(0,a.jsxs)(r.p,{children:["The worker RxStorage is a wrapper around any other RxStorage which allows to run the storage in a SharedWorker (only in browsers). By doing so, you can take CPU load from the main process and move it into the worker's process which can improve the perceived performance of your application. ",(0,a.jsx)(r.a,{href:"/rx-storage-shared-worker.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"remote",children:"Remote"}),"\n",(0,a.jsxs)(r.p,{children:["The Remote RxStorage is made to use a remote storage and communicate with it over an asynchronous message channel. The remote part could be on another JavaScript process or even on a different host machine. Mostly used internally in other storages like Worker or Electron-ipc. ",(0,a.jsx)(r.a,{href:"/rx-storage-remote.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"-sharding",children:"\ud83d\udc51 Sharding"}),"\n",(0,a.jsxs)(r.p,{children:["On some ",(0,a.jsx)(r.code,{children:"RxStorage"})," implementations (like IndexedDB), a huge performance improvement can be done by sharding the documents into multiple database instances. With the sharding plugin you can wrap any other ",(0,a.jsx)(r.code,{children:"RxStorage"})," into a sharded storage. ",(0,a.jsx)(r.a,{href:"/rx-storage-sharding.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"-memory-mapped",children:"\ud83d\udc51 Memory Mapped"}),"\n",(0,a.jsxs)(r.p,{children:["The memory-mapped ",(0,a.jsx)(r.a,{href:"/rx-storage.html",children:"RxStorage"})," is a wrapper around any other RxStorage. The wrapper creates an in-memory storage that is used for query and write operations. This memory instance stores its data in an underlying storage for persistence.\nThe main reason to use this is to improve query/write performance while still having the data stored on disc. ",(0,a.jsx)(r.a,{href:"/rx-storage-memory-mapped.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"-localstorage-meta-optimizer",children:"\ud83d\udc51 Localstorage Meta Optimizer"}),"\n",(0,a.jsxs)(r.p,{children:["The ",(0,a.jsx)(r.a,{href:"/rx-storage.html",children:"RxStorage"})," Localstorage Meta Optimizer is a wrapper around any other RxStorage. The wrapper uses the original RxStorage for normal collection documents. But to optimize the initial page load time, it uses ",(0,a.jsx)(r.a,{href:"/articles/localstorage.html",children:"localstorage"})," to store the plain key-value metadata that RxDB needs to create databases and collections. This plugin can only be used in browsers. ",(0,a.jsx)(r.a,{href:"/rx-storage-localstorage-meta-optimizer.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"electron-ipcrenderer--ipcmain",children:"Electron IpcRenderer & IpcMain"}),"\n",(0,a.jsxs)(r.p,{children:["To use RxDB in ",(0,a.jsx)(r.a,{href:"/electron-database.html",children:"electron"}),", it is recommended to run the RxStorage in the main process and the RxDatabase in the renderer processes. With the rxdb electron plugin you can create a remote RxStorage and consume it from the renderer process. ",(0,a.jsx)(r.a,{href:"/electron.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h3,{id:"third-party-based-storages",children:"Third Party based Storages"}),"\n",(0,a.jsx)(r.h4,{id:"-sqlite",children:"\ud83d\udc51 SQLite"}),"\n",(0,a.jsxs)(r.p,{children:["The SQLite storage has great performance when RxDB is used on ",(0,a.jsx)(r.strong,{children:"Node.js"}),", ",(0,a.jsx)(r.strong,{children:"Electron"}),", ",(0,a.jsx)(r.strong,{children:"React Native"}),", ",(0,a.jsx)(r.strong,{children:"Cordova"})," or ",(0,a.jsx)(r.strong,{children:"Capacitor"}),". ",(0,a.jsx)(r.a,{href:"/rx-storage-sqlite.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"dexiejs",children:"Dexie.js"}),"\n",(0,a.jsxs)(r.p,{children:["The Dexie.js based storage is based on the Dexie.js IndexedDB wrapper library. ",(0,a.jsx)(r.a,{href:"/rx-storage-dexie.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"mongodb",children:"MongoDB"}),"\n",(0,a.jsxs)(r.p,{children:["To use RxDB on the server side, the MongoDB RxStorage provides a way of having a secure, scalable and performant storage based on the popular MongoDB NoSQL database ",(0,a.jsx)(r.a,{href:"/rx-storage-mongodb.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"denokv",children:"DenoKV"}),"\n",(0,a.jsxs)(r.p,{children:["To use RxDB in Deno. The DenoKV RxStorage provides a way of having a secure, scalable and performant storage based on the Deno Key Value Store. ",(0,a.jsx)(r.a,{href:"/rx-storage-denokv.html",children:"Read more"})]}),"\n",(0,a.jsx)(r.h4,{id:"foundationdb",children:"FoundationDB"}),"\n",(0,a.jsxs)(r.p,{children:["To use RxDB on the server side, the FoundationDB RxStorage provides a way of having a secure, fault-tolerant and performant storage. ",(0,a.jsx)(r.a,{href:"/rx-storage-foundationdb.html",children:"Read more"})]})]})}function c(e={}){const{wrapper:r}={...(0,n.R)(),...e.components};return r?(0,a.jsx)(r,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},8453(e,r,s){s.d(r,{R:()=>i,x:()=>t});var o=s(6540);const a={},n=o.createContext(a);function i(e){const r=o.useContext(n);return o.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function t(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),o.createElement(n.Provider,{value:r},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/1f391b9e.825d8c20.js b/docs/assets/js/1f391b9e.825d8c20.js
deleted file mode 100644
index 80daef6114e..00000000000
--- a/docs/assets/js/1f391b9e.825d8c20.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6061],{7973(e,a,t){t.r(a),t.d(a,{default:()=>p});t(6540);var s=t(4164),d=t(5500),r=t(7559),i=t(8711),l=t(7910),n=t(7763),c=t(6896),o=t(2153);const m="mdxPageWrapper_j9I6";var x=t(4848);function p(e){const{content:a}=e,{metadata:t,assets:p}=a,{title:g,editUrl:h,description:j,frontMatter:A,lastUpdatedBy:v,lastUpdatedAt:u}=t,{keywords:_,wrapperClassName:b,hide_table_of_contents:w}=A,f=p.image??A.image,y=!!(h||u||v);return(0,x.jsx)(d.e3,{className:(0,s.A)(b??r.G.wrapper.mdxPages,r.G.page.mdxPage),children:(0,x.jsxs)(i.A,{children:[(0,x.jsx)(d.be,{title:g,description:j,keywords:_,image:f}),(0,x.jsx)("main",{className:"container container--fluid margin-vert--lg",children:(0,x.jsxs)("div",{className:(0,s.A)("row",m),children:[(0,x.jsxs)("div",{className:(0,s.A)("col",!w&&"col--8"),children:[(0,x.jsx)(c.A,{metadata:t}),(0,x.jsx)("article",{children:(0,x.jsx)(l.A,{children:(0,x.jsx)(a,{})})}),y&&(0,x.jsx)(o.A,{className:(0,s.A)("margin-top--sm",r.G.pages.pageFooterEditMetaRow),editUrl:h,lastUpdatedAt:u,lastUpdatedBy:v})]}),!w&&a.toc.length>0&&(0,x.jsx)("div",{className:"col col--2",children:(0,x.jsx)(n.A,{toc:a.toc,minHeadingLevel:A.toc_min_heading_level,maxHeadingLevel:A.toc_max_heading_level})})]})})]})})}},9057(e,a,t){t.d(a,{A:()=>n});var s=t(6540),d=t(8759),r=t(3230),i=t(8601),l=t(4848);const n={...d.A,code:function(e){return void 0!==e.children&&s.Children.toArray(e.children).every(e=>"string"==typeof e&&!e.includes("\n"))?(0,l.jsx)(r.A,{...e}):(0,l.jsx)("code",{...e})},pre:i.A}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/21fa2740.44a7d1df.js b/docs/assets/js/21fa2740.44a7d1df.js
deleted file mode 100644
index 76d3292bade..00000000000
--- a/docs/assets/js/21fa2740.44a7d1df.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5694],{8351(e,n,r){r.r(n),r.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>t,toc:()=>d});const t=JSON.parse('{"id":"releases/15.0.0","title":"RxDB 15.0.0 - Major Migration Overhaul","description":"Discover RxDB 15.0.0, featuring new migration strategies, replication improvements, and lightning-fast performance to supercharge your app.","source":"@site/docs/releases/15.0.0.md","sourceDirName":"releases","slug":"/releases/15.0.0.html","permalink":"/releases/15.0.0.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB 15.0.0 - Major Migration Overhaul","slug":"15.0.0.html","description":"Discover RxDB 15.0.0, featuring new migration strategies, replication improvements, and lightning-fast performance to supercharge your app."},"sidebar":"tutorialSidebar","previous":{"title":"16.0.0","permalink":"/releases/16.0.0.html"},"next":{"title":"14.0.0","permalink":"/releases/14.0.0.html"}}');var i=r(4848),s=r(8453);const o={title:"RxDB 15.0.0 - Major Migration Overhaul",slug:"15.0.0.html",description:"Discover RxDB 15.0.0, featuring new migration strategies, replication improvements, and lightning-fast performance to supercharge your app."},a="15.0.0",l={},d=[{value:"LinkedIn",id:"linkedin",level:2},{value:"Performance",id:"performance",level:2},{value:"Replication",id:"replication",level:2},{value:"Rewrite schema version migration",id:"rewrite-schema-version-migration",level:2},{value:"Set eventReduce:true as default",id:"set-eventreducetrue-as-default",level:2},{value:"Use crypto.subtle.digest for hashing",id:"use-cryptosubtledigest-for-hashing",level:2},{value:"Fix attachment hashing",id:"fix-attachment-hashing",level:2},{value:"Requires at least typescript version 5.0.0",id:"requires-at-least-typescript-version-500",level:2},{value:"Require string based $regex",id:"require-string-based-regex",level:2},{value:"Refactor dexie.js RxStorage",id:"refactor-dexiejs-rxstorage",level:2},{value:"RxLocalDocument.$ emits a document instance, not the plain data",id:"rxlocaldocument-emits-a-document-instance-not-the-plain-data",level:2},{value:"Fix return type of .bulkUpsert",id:"fix-return-type-of-bulkupsert",level:2},{value:"Add dev-mode check for disallowed $ref fields",id:"add-dev-mode-check-for-disallowed-ref-fields",level:2},{value:"Improve RxDocument property access performance",id:"improve-rxdocument-property-access-performance",level:2},{value:"Add deno support",id:"add-deno-support",level:2},{value:"Memory RxStorage",id:"memory-rxstorage",level:2},{value:"Memory-Synced storage no longer supports replication+migration",id:"memory-synced-storage-no-longer-supports-replicationmigration",level:2},{value:"Added Logger Plugin",id:"added-logger-plugin",level:2},{value:"Documentation is now served by docusaurus",id:"documentation-is-now-served-by-docusaurus",level:2},{value:"Replaced modfijs with mingo package",id:"replaced-modfijs-with-mingo-package",level:2},{value:"Changes to the RxStorage interface",id:"changes-to-the-rxstorage-interface",level:2},{value:"Other changes",id:"other-changes",level:2},{value:"Changes to the \ud83d\udc51 Premium Plugins",id:"changes-to-the--premium-plugins",level:2},{value:"storage-migration plugin moved from premium to open-core",id:"storage-migration-plugin-moved-from-premium-to-open-core",level:3},{value:"Changes in pricing",id:"changes-in-pricing",level:3},{value:"Added perpetual license option",id:"added-perpetual-license-option",level:3},{value:"You can help!",id:"you-can-help",level:2}];function h(e){const n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"1500",children:"15.0.0"})}),"\n",(0,i.jsxs)(n.p,{children:["The release ",(0,i.jsx)(n.a,{href:"https://rxdb.info/releases/15.0.0.html",children:"15.0.0"})," is used for major refactorings in the migration plugins and performance improvements of the RxStorage implementations."]}),"\n",(0,i.jsx)(n.h2,{id:"linkedin",children:"LinkedIn"}),"\n",(0,i.jsxs)(n.p,{children:["Stay connected with the latest updates and network with professionals in the RxDB community by following RxDB's ",(0,i.jsx)(n.a,{href:"https://www.linkedin.com/company/rxdb",children:"official LinkedIn page"}),"!"]}),"\n",(0,i.jsx)(n.h2,{id:"performance",children:"Performance"}),"\n",(0,i.jsxs)(n.p,{children:["Performance has improved a lot. Much work has been done to reduce the CPU footprint of RxDB so that when writes and reads are performed on the database, the JavaScript process can start updating the UI much faster.\nAlso there have been a lot of improvements to the ",(0,i.jsx)(n.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"})," which now runs in a ",(0,i.jsx)(n.strong,{children:"Write-Ahead Logging (WAL)"})," mode which makes write operations about 4x as fast.\nThe whole RxStorage interface has been optimized so that it has to run less operations which improves overall performance. ",(0,i.jsx)(n.a,{href:"/rx-storage-performance.html",children:"Read more"})]}),"\n",(0,i.jsx)("a",{href:"../rx-storage-performance.html",children:(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"600"})})}),"\n",(0,i.jsx)(n.h2,{id:"replication",children:"Replication"}),"\n",(0,i.jsxs)(n.p,{children:["Replication options (like url), are no longer used as replication state identifier. Changing the url without having to restart the replication, is now possible. This is useful if you change your replication url (like ",(0,i.jsx)(n.code,{children:"path/v3/"}),") on schema changes and you do not want to restart the replication from scratch. You can even swap the replication plugin while still keeping the replication state. The ",(0,i.jsx)(n.a,{href:"/replication-couchdb.html",children:"couchdb replication"})," now also requires an ",(0,i.jsx)(n.code,{children:"replicationIdentifier"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["The replication meta data is now also compressed when the ",(0,i.jsx)(n.a,{href:"/key-compression.html",children:"KeyCompression Plugin"})," is used."]}),"\n",(0,i.jsx)(n.p,{children:"The replication-protocol does now support attachment replication. This clears the path to add the attachment replication to the other RxDB replication plugins."}),"\n",(0,i.jsx)(n.h2,{id:"rewrite-schema-version-migration",children:"Rewrite schema version migration"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"/migration-schema.html",children:"schema migration plugin"})," has been fully rewritten from scratch."]}),"\n",(0,i.jsx)(n.p,{children:"From now on it internally uses the replication protocol to do a one-time replication from the old collection to the new one. This makes the code more simple and ensures that canceled migrations (when the user closes the browser), can continue from the correct position."}),"\n",(0,i.jsxs)(n.p,{children:["Replication states from the ",(0,i.jsx)(n.a,{href:"/replication.html",children:"RxReplication"})," are also migrated together with the normal data.\nPreviously a migration dropped the replication state which required a new replication of all data from scratch, even if the\nclient already had the same data as the server. Now the ",(0,i.jsx)(n.code,{children:"assumedMasterState"})," and ",(0,i.jsx)(n.code,{children:"checkpoint"})," are also migrated so that\nthe replication will continue from where it was before the migration has run."]}),"\n",(0,i.jsx)(n.p,{children:"Also it now handles multi-instance runtimes correctly. If multiple browser tabs are open, only one of them (per RxCollection) will run the migration.\nMigration state events are propagated across browser tabs."}),"\n",(0,i.jsxs)(n.p,{children:["Documents with ",(0,i.jsx)(n.code,{children:"_deleted: true"})," will also be migrated. This ensures that non-pushed deletes are not dropped during migrations and will\nstill be replicated if the client goes online again."]}),"\n",(0,i.jsxs)(n.h2,{id:"set-eventreducetrue-as-default",children:["Set ",(0,i.jsx)(n.code,{children:"eventReduce:true"})," as default"]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce algorithm"})," is now enabled by default."]}),"\n",(0,i.jsxs)(n.h2,{id:"use-cryptosubtledigest-for-hashing",children:["Use ",(0,i.jsx)(n.code,{children:"crypto.subtle.digest"})," for hashing"]}),"\n",(0,i.jsxs)(n.p,{children:["Using ",(0,i.jsx)(n.code,{children:"crypto.subtle.digest"})," from the native WebCrypto API is much faster, so RxDB now uses that as a default. If the API is not available, like in React-Native, the ",(0,i.jsx)(n.a,{href:"https://github.com/unjs/ohash",children:"ohash"})," module is used instead. Also any custom ",(0,i.jsx)(n.code,{children:"hashFunction"})," can be provided when creating the ",(0,i.jsx)(n.a,{href:"/rx-database.html",children:"RxDatabase"}),". The ",(0,i.jsx)(n.code,{children:"hashFunction"})," must now be async and return a Promise."]}),"\n",(0,i.jsx)(n.h2,{id:"fix-attachment-hashing",children:"Fix attachment hashing"}),"\n",(0,i.jsxs)(n.p,{children:["Hashing of attachment data to calculate the ",(0,i.jsx)(n.code,{children:"digest"})," is now done from the RxDB side, not the RxStorage. If you set a custom ",(0,i.jsx)(n.code,{children:"hashFunction"})," for the database, it will also be used for attachments ",(0,i.jsx)(n.code,{children:"digest"})," meta data."]}),"\n",(0,i.jsx)(n.h2,{id:"requires-at-least-typescript-version-500",children:"Requires at least typescript version 5.0.0"}),"\n",(0,i.jsxs)(n.p,{children:["We now use ",(0,i.jsx)(n.code,{children:"export type * from './types';"})," so RxDB will not work on typescript versions older than 5.0.0."]}),"\n",(0,i.jsxs)(n.h2,{id:"require-string-based-regex",children:["Require string based ",(0,i.jsx)(n.code,{children:"$regex"})]}),"\n",(0,i.jsxs)(n.p,{children:["Queries with a ",(0,i.jsx)(n.code,{children:"$regex"})," operator must now be defined as strings, not with ",(0,i.jsx)(n.code,{children:"RegExp"})," objects. You can still pass RegExp's flags in ",(0,i.jsx)(n.code,{children:"$options"})," parameter. ",(0,i.jsx)(n.code,{children:"RegExp"})," are mutable objects, which was dangerous and caused hard-to-debug problems.\nAlso stringification of the $regex had bad performance but is required to send queries from RxDB to the RxStorage."]}),"\n",(0,i.jsx)(n.h2,{id:"refactor-dexiejs-rxstorage",children:"Refactor dexie.js RxStorage"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"/rx-storage-dexie.html",children:"dexie.js storage"})," was refactored to add some missing features:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rx-attachment.html",children:"Attachment"})," support"]}),"\n",(0,i.jsx)(n.li,{children:"Support for boolean indexes"}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"rxlocaldocument-emits-a-document-instance-not-the-plain-data",children:"RxLocalDocument.$ emits a document instance, not the plain data"}),"\n",(0,i.jsxs)(n.p,{children:["This was changed in ",(0,i.jsx)(n.a,{href:"/releases/14.0.0.html",children:"v14"})," for a normal RxDocument.$ which emits RxDocument instances. Same is now also done for ",(0,i.jsx)(n.a,{href:"/rx-local-document.html",children:"local documents"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"fix-return-type-of-bulkupsert",children:"Fix return type of .bulkUpsert"}),"\n",(0,i.jsxs)(n.p,{children:["Equal to other bulk operations, ",(0,i.jsx)(n.code,{children:"bulkUpsert"})," will now return an ",(0,i.jsx)(n.code,{children:"error"})," and a ",(0,i.jsx)(n.code,{children:"success"})," array. This allows to filter for validation errors and handle them properly."]}),"\n",(0,i.jsx)(n.h2,{id:"add-dev-mode-check-for-disallowed-ref-fields",children:"Add dev-mode check for disallowed $ref fields"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB cannot resolve ",(0,i.jsx)(n.code,{children:"$ref"})," fields in the schema because it would have a negative performance impact.\nWe now have a dev-mode check to throw a helpful error message if $refs are used in the schema."]}),"\n",(0,i.jsx)(n.h2,{id:"improve-rxdocument-property-access-performance",children:"Improve RxDocument property access performance"}),"\n",(0,i.jsxs)(n.p,{children:["We now use the Proxy API instead of defining getters on each nested property. Also fixed ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/pull/4949",children:"#4949"})]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"patternProperties"})," is now allowed on the non-top-level of a schema ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/pull/4951",children:"#4951"})]}),"\n",(0,i.jsx)(n.h2,{id:"add-deno-support",children:"Add deno support"}),"\n",(0,i.jsxs)(n.p,{children:["The RxDB test suite now also runs in the ",(0,i.jsx)(n.a,{href:"https://deno.com/",children:"deno"})," runtime. Also there is a ",(0,i.jsx)(n.a,{href:"https://rxdb.info/rx-storage-denokv.html",children:"DenoKV"})," based RxStorage to use with Deno Deploy."]}),"\n",(0,i.jsx)(n.h2,{id:"memory-rxstorage",children:"Memory RxStorage"}),"\n",(0,i.jsxs)(n.p,{children:["Rewrites of the ",(0,i.jsx)(n.a,{href:"/rx-storage-memory.html",children:"Memory RxStorage"})," for better performance."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Writes are 3x faster"}),"\n",(0,i.jsx)(n.li,{children:"Find-by id is 2x faster"}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"memory-synced-storage-no-longer-supports-replicationmigration",children:"Memory-Synced storage no longer supports replication+migration"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"/rx-storage-memory-synced.html",children:"memory-synced storage"})," itself does not support replication and migration. This was allowed in the past, but dangerous so now there is an error that throws.\nInstead you should replicate the underlying parent storage. Notice that this is only for the ",(0,i.jsx)(n.a,{href:"/rx-storage-memory-synced.html",children:"memory-synced storage"}),", NOT for the normal ",(0,i.jsx)(n.a,{href:"/rx-storage-memory.html",children:"memory storage"}),". There the replication works like before."]}),"\n",(0,i.jsx)(n.h2,{id:"added-logger-plugin",children:"Added Logger Plugin"}),"\n",(0,i.jsxs)(n.p,{children:["I added a ",(0,i.jsx)(n.a,{href:"/logger.html",children:"logger plugin"})," to detect performance problems and errors."]}),"\n",(0,i.jsx)(n.h2,{id:"documentation-is-now-served-by-docusaurus",children:"Documentation is now served by docusaurus"}),"\n",(0,i.jsxs)(n.p,{children:["In the past we used gitbook which is no longer maintained and had some major issues. Now the documentation of RxDB is rendered and served with ",(0,i.jsx)(n.a,{href:"https://docusaurus.io/",children:"docusaurus"})," which has a better design and maintenance."]}),"\n",(0,i.jsxs)(n.h2,{id:"replaced-modfijs-with-mingo-package",children:["Replaced ",(0,i.jsx)(n.code,{children:"modfijs"})," with ",(0,i.jsx)(n.code,{children:"mingo"})," package"]}),"\n",(0,i.jsxs)(n.p,{children:["In the past, the ",(0,i.jsx)(n.a,{href:"https://github.com/lgandecki/modifyjs",children:"modifyjs"})," was used for the ",(0,i.jsx)(n.code,{children:"update"})," plugin. This was replaced with the ",(0,i.jsx)(n.a,{href:"https://github.com/kofrasa/mingo",children:"mingo library"})," which is more up to date and already used in RxDB for the query engine."]}),"\n",(0,i.jsx)(n.h2,{id:"changes-to-the-rxstorage-interface",children:"Changes to the RxStorage interface"}),"\n",(0,i.jsxs)(n.p,{children:["We no longer have ",(0,i.jsx)(n.code,{children:"RxStorage.statics.prepareQuery()"}),". Instead all storages get the same prepared query as input for the ",(0,i.jsx)(n.code,{children:".query()"})," method. If a storage requires some transformations, it has to do them by itself before running the query. This change simplifies the whole RxDB code base a lot and the previous assumption of having a better performance by pre-running the query preparation, turned out to be not true because the query planning is quite fast."]}),"\n",(0,i.jsxs)(n.p,{children:["Removed the ",(0,i.jsx)(n.code,{children:"RxStorage.statics"})," property. This makes configuration easier especially for the remote storage plugins."]}),"\n",(0,i.jsxs)(n.p,{children:["The RxStorage itself will now return ",(0,i.jsx)(n.code,{children:"_deleted=true"})," documents on the ",(0,i.jsx)(n.code,{children:".query()"})," method. This is required for upcoming plugins like the server plugin where it must be able to run queries on deleted documents.\nNotice that this is only for the RxStorage itself, RxDB queries will run like normal and NOT contain deleted documents in their results."]}),"\n",(0,i.jsx)(n.p,{children:"Changed the response type of RxStorageInstance.bulkWrite() from indexed (by id) objects to arrays for better performance."}),"\n",(0,i.jsx)(n.h2,{id:"other-changes",children:"Other changes"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Added ",(0,i.jsx)(n.code,{children:"RxCollection.cleanup()"})," to manually call the ",(0,i.jsx)(n.a,{href:"/cleanup.html",children:"cleanup functions"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Rename send$ to sent$: ",(0,i.jsx)(n.code,{children:"myRxReplicationState.send$.subscribe"})," works only if the sending is successful. Therefore, it is renamed to ",(0,i.jsx)(n.code,{children:"sent$"}),", not ",(0,i.jsx)(n.code,{children:"send$"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["We no longer ship ",(0,i.jsx)(n.code,{children:"dist/rxdb.browserify.js"})," and ",(0,i.jsx)(n.code,{children:"dist/rxdb.browserify.min.js"}),". If you need these, build them by yourself."]}),"\n",(0,i.jsx)(n.li,{children:"The example project for vanilla javascript was outdated. I removed it to no longer confuse new users."}),"\n",(0,i.jsxs)(n.li,{children:["REPLACE ",(0,i.jsx)(n.code,{children:"new Date().getTime()"})," with ",(0,i.jsx)(n.code,{children:"Date.now()"})," which is ",(0,i.jsx)(n.a,{href:"https://stackoverflow.com/questions/12517359/performance-date-now-vs-date-gettime",children:"2x faster"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["Renamed replication-p2p to replication-webrtc. I will add more p2p replication plugins in the future, which are not based on ",(0,i.jsx)(n.a,{href:"/replication-webrtc.html",children:"WebRTC"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["REMOVED ",(0,i.jsx)(n.code,{children:"RxChangeEvent.eventId"}),". If you really need a unique ID, you can craft your own one based on the document ",(0,i.jsx)(n.code,{children:"_rev"})," and ",(0,i.jsx)(n.code,{children:"primary"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["REMOVED ",(0,i.jsx)(n.code,{children:"RxChangeEvent.startTime"})," and ",(0,i.jsx)(n.code,{children:"RxChangeEvent.endTime"})," so we do not have to call ",(0,i.jsx)(n.code,{children:"Date.now()"})," once per write row."]}),"\n",(0,i.jsxs)(n.li,{children:["ADDED ",(0,i.jsx)(n.code,{children:"EventBulk.startTime"})," and ",(0,i.jsx)(n.code,{children:"EventBulk.endTime"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["FIX ",(0,i.jsx)(n.code,{children:"database.remove()"})," does not work on databases with encrypted fields."]}),"\n",(0,i.jsxs)(n.li,{children:["FIX ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/pull/5187",children:"react-native: replaceAll is not a function"})]}),"\n",(0,i.jsx)(n.li,{children:"FIX Throttle calls to forkInstance on push-replication to not cause memory spikes and lagging UI"}),"\n",(0,i.jsxs)(n.li,{children:["FIX PushModifier applied to pre-change legacy document, resulting in old document sent to endpoint ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/5256",children:"#5256"})]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rx-attachment.html#attachment-compression",children:"Attachment compression"})," is now using the native ",(0,i.jsx)(n.code,{children:"Compression Streams API"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["FIX ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/5311",children:"#5311"})," URL.createObjectURL is not a function in a browser plugin environment(background.js)"]}),"\n",(0,i.jsxs)(n.li,{children:["FIX ",(0,i.jsx)(n.code,{children:"structuredClone"})," not available in ReactNative ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/5046#issuecomment-1827374498",children:"#5046"})]}),"\n",(0,i.jsxs)(n.li,{children:["The following things moved out of beta:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"/replication-firestore.html",children:"Firestore replication"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"/replication-webrtc.html",children:"WebRTC replication"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"/replication-nats.html",children:"NATS replication"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"changes-to-the--premium-plugins",children:"Changes to the \ud83d\udc51 Premium Plugins"}),"\n",(0,i.jsx)(n.h3,{id:"storage-migration-plugin-moved-from-premium-to-open-core",children:"storage-migration plugin moved from premium to open-core"}),"\n",(0,i.jsxs)(n.p,{children:["The storage migration plugin can be used to migrate data between different RxStorage implementation or to migrate data between major RxDB versions. This previously was a \ud83d\udc51 premium plugin, but now it is part of the open-core. Also the params syntax changed a bit ",(0,i.jsx)(n.a,{href:"/migration-storage.html",children:"read more"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"changes-in-pricing",children:"Changes in pricing"}),"\n",(0,i.jsx)(n.p,{children:"The pricing of the premium plugins was changed. This makes it cheaper for smaller companies and single individuals."}),"\n",(0,i.jsx)(n.h3,{id:"added-perpetual-license-option",children:"Added perpetual license option"}),"\n",(0,i.jsxs)(n.p,{children:["By default you are not allowed to use the premium plugins after the\nlicense has expired and you will no longer be able to install them. But\nyou can choose the ",(0,i.jsx)(n.strong,{children:"Perpetual license"})," option. With the perpetual\nlicense option, you can still use the plugins even after the license is\nexpired. But you will no longer get any updates from newer RxDB\nversions."]}),"\n",(0,i.jsx)(n.h2,{id:"you-can-help",children:"You can help!"}),"\n",(0,i.jsxs)(n.p,{children:["There are many things that can be done by ",(0,i.jsx)(n.strong,{children:"you"})," to improve RxDB:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Check the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/BACKLOG.md",children:"BACKLOG"})," for features that would be great to have."]}),"\n",(0,i.jsxs)(n.li,{children:["Check the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/before-next-major.md",children:"breaking backlog"})," for breaking changes that must be implemented in the future but where I did not have the time yet."]}),"\n",(0,i.jsxs)(n.li,{children:["Check the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/search?q=todo",children:"todos"})," in the code. There are many small improvements that can be done for performance and build size."]}),"\n",(0,i.jsx)(n.li,{children:"Review the code and add tests. I am only a single human with a laptop. My code is not perfect and much small improvements can be done when people review the code and help me to clarify undefined behaviors."}),"\n",(0,i.jsxs)(n.li,{children:["Update the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"example projects"})," some of them are outdated and need updates."]}),"\n"]})]})}function c(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,n,r){r.d(n,{R:()=>o,x:()=>a});var t=r(6540);const i={},s=t.createContext(i);function o(e){const n=t.useContext(s);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),t.createElement(s.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/22dd74f7.8c39a7a2.js b/docs/assets/js/22dd74f7.8c39a7a2.js
deleted file mode 100644
index 2803c7ebbf2..00000000000
--- a/docs/assets/js/22dd74f7.8c39a7a2.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1567],{5226(e){e.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"category","label":"Getting Started","collapsed":false,"items":[{"type":"link","href":"/overview.html","label":"Overview","docId":"overview","unlisted":false},{"type":"link","href":"/quickstart.html","label":"\ud83d\ude80 Quickstart","docId":"quickstart","unlisted":false},{"type":"link","href":"/install.html","label":"Installation","docId":"install","unlisted":false},{"type":"link","href":"/dev-mode.html","label":"Development Mode","docId":"dev-mode","unlisted":false},{"type":"link","href":"/tutorials/typescript.html","label":"TypeScript Setup","docId":"tutorials/typescript","unlisted":false}],"collapsible":true},{"type":"category","label":"Core Entities","collapsed":true,"items":[{"type":"link","href":"/rx-database.html","label":"RxDatabase","docId":"rx-database","unlisted":false},{"type":"link","href":"/rx-schema.html","label":"RxSchema","docId":"rx-schema","unlisted":false},{"type":"link","href":"/rx-collection.html","label":"RxCollection","docId":"rx-collection","unlisted":false},{"type":"link","href":"/rx-document.html","label":"RxDocument","docId":"rx-document","unlisted":false},{"type":"link","href":"/rx-query.html","label":"RxQuery","docId":"rx-query","unlisted":false}],"collapsible":true},{"type":"category","label":"\ud83d\udcbe Storages","items":[{"type":"link","href":"/rx-storage.html","label":"RxStorage Overview","docId":"rx-storage","unlisted":false},{"type":"link","href":"/rx-storage-localstorage.html","label":"LocalStorage (Browser)","docId":"rx-storage-localstorage","unlisted":false},{"type":"link","href":"/rx-storage-indexeddb.html","label":"IndexedDB \ud83d\udc51 (Browser, Capacitor)","docId":"rx-storage-indexeddb","unlisted":false},{"type":"link","href":"/rx-storage-opfs.html","label":"OPFS \ud83d\udc51 (Browser)","docId":"rx-storage-opfs","unlisted":false},{"type":"link","href":"/rx-storage-memory.html","label":"Memory","docId":"rx-storage-memory","unlisted":false},{"type":"link","href":"/rx-storage-filesystem-node.html","label":"Filesystem Node \ud83d\udc51 (Node.js)","docId":"rx-storage-filesystem-node","unlisted":false},{"type":"link","href":"/rx-storage-sqlite.html","label":"SQLite (Capacitor, React-Native, Expo, Tauri, Electron, Node.js)","docId":"rx-storage-sqlite","unlisted":false},{"type":"category","label":"Third Party Storages","items":[{"type":"link","href":"/rx-storage-dexie.html","label":"Dexie.js","docId":"rx-storage-dexie","unlisted":false},{"type":"link","href":"/rx-storage-mongodb.html","label":"MongoDB","docId":"rx-storage-mongodb","unlisted":false},{"type":"link","href":"/rx-storage-denokv.html","label":"DenoKV","docId":"rx-storage-denokv","unlisted":false},{"type":"link","href":"/rx-storage-foundationdb.html","label":"FoundationDB","docId":"rx-storage-foundationdb","unlisted":false}],"collapsed":true,"collapsible":true}],"collapsed":true,"collapsible":true},{"type":"category","label":"Storage Wrappers","items":[{"type":"link","href":"/schema-validation.html","label":"Schema Validation","docId":"schema-validation","unlisted":false},{"type":"link","href":"/encryption.html","label":"Encryption","docId":"encryption","unlisted":false},{"type":"link","href":"/key-compression.html","label":"Key Compression","docId":"key-compression","unlisted":false},{"type":"link","href":"/logger.html","label":"Logger \ud83d\udc51","docId":"logger","unlisted":false},{"type":"link","href":"/rx-storage-remote.html","label":"Remote RxStorage","docId":"rx-storage-remote","unlisted":false},{"type":"link","href":"/rx-storage-worker.html","label":"Worker RxStorage \ud83d\udc51","docId":"rx-storage-worker","unlisted":false},{"type":"link","href":"/rx-storage-shared-worker.html","label":"SharedWorker RxStorage \ud83d\udc51","docId":"rx-storage-shared-worker","unlisted":false},{"type":"link","href":"/rx-storage-memory-mapped.html","label":"Memory Mapped RxStorage \ud83d\udc51","docId":"rx-storage-memory-mapped","unlisted":false},{"type":"link","href":"/rx-storage-sharding.html","label":"Sharding \ud83d\udc51","docId":"rx-storage-sharding","unlisted":false},{"type":"link","href":"/rx-storage-localstorage-meta-optimizer.html","label":"Localstorage Meta Optimizer \ud83d\udc51","docId":"rx-storage-localstorage-meta-optimizer","unlisted":false},{"type":"link","href":"/electron.html","label":"Electron","docId":"electron","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"category","label":"\ud83d\udd04 Replication","items":[{"type":"link","href":"/replication.html","label":"\u2699\ufe0f Sync Engine","docId":"replication","unlisted":false},{"type":"link","href":"/replication-http.html","label":"HTTP Replication","docId":"replication-http","unlisted":false},{"type":"link","href":"/replication-server.html","label":"RxServer Replication","docId":"replication-server","unlisted":false},{"type":"link","href":"/replication-graphql.html","label":"GraphQL Replication","docId":"replication-graphql","unlisted":false},{"type":"link","href":"/replication-websocket.html","label":"WebSocket Replication","docId":"replication-websocket","unlisted":false},{"type":"link","href":"/replication-couchdb.html","label":"CouchDB Replication","docId":"replication-couchdb","unlisted":false},{"type":"link","href":"/replication-webrtc.html","label":"WebRTC P2P Replication","docId":"replication-webrtc","unlisted":false},{"type":"link","href":"/replication-firestore.html","label":"Firestore Replication","docId":"replication-firestore","unlisted":false},{"type":"link","href":"/replication-mongodb.html","label":"MongoDB Replication","docId":"replication-mongodb","unlisted":false},{"type":"link","href":"/replication-supabase.html","label":"Supabase Replication","docId":"replication-supabase","unlisted":false},{"type":"link","href":"/replication-nats.html","label":"NATS Replication","docId":"replication-nats","unlisted":false},{"type":"link","href":"/replication-appwrite.html","label":"Appwrite Replication","docId":"replication-appwrite","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"category","label":"Server","items":[{"type":"link","href":"/rx-server.html","label":"RxServer","docId":"rx-server","unlisted":false},{"type":"link","href":"/rx-server-scaling.html","label":"RxServer Scaling","docId":"rx-server-scaling","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"category","label":"How RxDB works","items":[{"type":"link","href":"/transactions-conflicts-revisions.html","label":"Transactions Conflicts Revisions","docId":"transactions-conflicts-revisions","unlisted":false},{"type":"link","href":"/query-cache.html","label":"Query Cache","docId":"query-cache","unlisted":false},{"type":"link","href":"/plugins.html","label":"Creating Plugins","docId":"plugins","unlisted":false},{"type":"link","href":"/errors.html","label":"Errors","docId":"errors","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"category","label":"Advanced Features","items":[{"type":"category","label":"Migration","items":[{"type":"link","href":"/migration-schema.html","label":"Schema Migration","docId":"migration-schema","unlisted":false},{"type":"link","href":"/migration-storage.html","label":"Storage Migration","docId":"migration-storage","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"link","href":"/rx-attachment.html","label":"Attachments","docId":"rx-attachment","unlisted":false},{"type":"link","href":"/rx-pipeline.html","label":"RxPipelines","docId":"rx-pipeline","unlisted":false},{"type":"link","href":"/reactivity.html","label":"Custom Reactivity","docId":"reactivity","unlisted":false},{"type":"link","href":"/rx-state.html","label":"RxState","docId":"rx-state","unlisted":false},{"type":"link","href":"/rx-local-document.html","label":"Local Documents","docId":"rx-local-document","unlisted":false},{"type":"link","href":"/cleanup.html","label":"Cleanup","docId":"cleanup","unlisted":false},{"type":"link","href":"/backup.html","label":"Backup","docId":"backup","unlisted":false},{"type":"link","href":"/leader-election.html","label":"Leader Election","docId":"leader-election","unlisted":false},{"type":"link","href":"/middleware.html","label":"Middleware","docId":"middleware","unlisted":false},{"type":"link","href":"/crdt.html","label":"CRDT","docId":"crdt","unlisted":false},{"type":"link","href":"/population.html","label":"Population","docId":"population","unlisted":false},{"type":"link","href":"/orm.html","label":"ORM","docId":"orm","unlisted":false},{"type":"link","href":"/fulltext-search.html","label":"Fulltext Search \ud83d\udc51","docId":"fulltext-search","unlisted":false},{"type":"link","href":"/articles/javascript-vector-database.html","label":"Vector Database","docId":"articles/javascript-vector-database","unlisted":false},{"type":"link","href":"/query-optimizer.html","label":"Query Optimizer \ud83d\udc51","docId":"query-optimizer","unlisted":false},{"type":"link","href":"/third-party-plugins.html","label":"Third Party Plugins","docId":"third-party-plugins","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"category","label":"Integrations","items":[{"type":"link","href":"/react.html","label":"React","docId":"react","unlisted":false},{"type":"link","label":"TanStack DB","href":"https://tanstack.com/db/latest/docs/collections/rxdb-collection","customProps":{"target":"_blank"}}],"collapsed":true,"collapsible":true},{"type":"category","label":"Performance","items":[{"type":"link","href":"/rx-storage-performance.html","label":"RxStorage Performance","docId":"rx-storage-performance","unlisted":false},{"type":"link","href":"/nosql-performance-tips.html","label":"NoSQL Performance Tips","docId":"nosql-performance-tips","unlisted":false},{"type":"link","href":"/slow-indexeddb.html","label":"Slow IndexedDB","docId":"slow-indexeddb","unlisted":false},{"type":"link","href":"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html","label":"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite","docId":"articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"category","label":"Releases","items":[{"type":"link","href":"/releases/17.0.0.html","label":"17.0.0","docId":"releases/17.0.0","unlisted":false},{"type":"link","href":"/releases/16.0.0.html","label":"16.0.0","docId":"releases/16.0.0","unlisted":false},{"type":"link","href":"/releases/15.0.0.html","label":"15.0.0","docId":"releases/15.0.0","unlisted":false},{"type":"link","href":"/releases/14.0.0.html","label":"14.0.0","docId":"releases/14.0.0","unlisted":false},{"type":"link","href":"/releases/13.0.0.html","label":"13.0.0","docId":"releases/13.0.0","unlisted":false},{"type":"link","href":"/releases/12.0.0.html","label":"12.0.0","docId":"releases/12.0.0","unlisted":false},{"type":"link","href":"/releases/11.0.0.html","label":"11.0.0","docId":"releases/11.0.0","unlisted":false},{"type":"link","href":"/releases/10.0.0.html","label":"10.0.0","docId":"releases/10.0.0","unlisted":false},{"type":"link","href":"/releases/9.0.0.html","label":"9.0.0","docId":"releases/9.0.0","unlisted":false},{"type":"link","href":"/releases/8.0.0.html","label":"8.0.0","docId":"releases/8.0.0","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"category","label":"Articles","items":[{"type":"link","href":"/articles/browser-database.html","label":"Benefits of RxDB & Browser Databases","docId":"articles/browser-database","unlisted":false},{"type":"link","href":"/articles/local-first-future.html","label":"Why Local-First Software Is the Future and its Limitations","docId":"articles/local-first-future","unlisted":false},{"type":"link","href":"/why-nosql.html","label":"Why NoSQL Powers Modern UI Apps","docId":"why-nosql","unlisted":false},{"type":"link","href":"/downsides-of-offline-first.html","label":"Downsides of Local First / Offline First","docId":"downsides-of-offline-first","unlisted":false},{"type":"link","href":"/nodejs-database.html","label":"RxDB - The Real-Time Database for Node.js","docId":"nodejs-database","unlisted":false},{"type":"link","href":"/offline-first.html","label":"Local First / Offline First","docId":"offline-first","unlisted":false},{"type":"link","href":"/react-native-database.html","label":"React Native Database - Sync & Store Like a Pro","docId":"react-native-database","unlisted":false},{"type":"link","href":"/articles/angular-database.html","label":"RxDB as a Database in an Angular Application","docId":"articles/angular-database","unlisted":false},{"type":"link","href":"/articles/browser-storage.html","label":"Browser Storage - RxDB as a Database for Browsers","docId":"articles/browser-storage","unlisted":false},{"type":"link","href":"/articles/data-base.html","label":"Empower Web Apps with Reactive RxDB Data-base","docId":"articles/data-base","unlisted":false},{"type":"link","href":"/articles/embedded-database.html","label":"Embedded Database, Real-time Speed - RxDB","docId":"articles/embedded-database","unlisted":false},{"type":"link","href":"/articles/flutter-database.html","label":"Supercharge Flutter Apps with the RxDB Database","docId":"articles/flutter-database","unlisted":false},{"type":"link","href":"/articles/frontend-database.html","label":"RxDB - The Ultimate JS Frontend Database","docId":"articles/frontend-database","unlisted":false},{"type":"link","href":"/articles/in-memory-nosql-database.html","label":"RxDB In-Memory NoSQL - Supercharge Real-Time Apps","docId":"articles/in-memory-nosql-database","unlisted":false},{"type":"link","href":"/articles/ionic-database.html","label":"RxDB - The Perfect Ionic Database","docId":"articles/ionic-database","unlisted":false},{"type":"link","href":"/articles/ionic-storage.html","label":"RxDB - Local Ionic Storage with Encryption, Compression & Sync","docId":"articles/ionic-storage","unlisted":false},{"type":"link","href":"/articles/json-database.html","label":"RxDB - The JSON Database Built for JavaScript","docId":"articles/json-database","unlisted":false},{"type":"link","href":"/articles/websockets-sse-polling-webrtc-webtransport.html","label":"WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport","docId":"articles/websockets-sse-polling-webrtc-webtransport","unlisted":false},{"type":"link","href":"/articles/localstorage.html","label":"Using localStorage in Modern Applications - A Comprehensive Guide","docId":"articles/localstorage","unlisted":false},{"type":"link","href":"/articles/mobile-database.html","label":"Real-Time & Offline - RxDB for Mobile Apps","docId":"articles/mobile-database","unlisted":false},{"type":"link","href":"/articles/progressive-web-app-database.html","label":"RxDB as a Database for Progressive Web Apps (PWA)","docId":"articles/progressive-web-app-database","unlisted":false},{"type":"link","href":"/articles/react-database.html","label":"RxDB as a Database for React Applications","docId":"articles/react-database","unlisted":false},{"type":"link","href":"/articles/realtime-database.html","label":"What Really Is a Realtime Database?","docId":"articles/realtime-database","unlisted":false},{"type":"link","href":"/articles/angular-indexeddb.html","label":"Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone","docId":"articles/angular-indexeddb","unlisted":false},{"type":"link","href":"/articles/react-indexeddb.html","label":"IndexedDB Database in React Apps - The Power of RxDB","docId":"articles/react-indexeddb","unlisted":false},{"type":"link","href":"/capacitor-database.html","label":"Capacitor Database Guide - SQLite, RxDB & More","docId":"capacitor-database","unlisted":false},{"type":"link","href":"/alternatives.html","label":"Alternatives for realtime local-first JavaScript applications and local databases","docId":"alternatives","unlisted":false},{"type":"link","href":"/electron-database.html","label":"Electron Database - Storage adapters for SQLite, Filesystem and In-Memory","docId":"electron-database","unlisted":false},{"type":"link","href":"/articles/optimistic-ui.html","label":"Building an Optimistic UI with RxDB","docId":"articles/optimistic-ui","unlisted":false},{"type":"link","href":"/articles/local-database.html","label":"What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications","docId":"articles/local-database","unlisted":false},{"type":"link","href":"/articles/react-native-encryption.html","label":"React Native Encryption and Encrypted Database/Storage","docId":"articles/react-native-encryption","unlisted":false},{"type":"link","href":"/articles/vue-database.html","label":"RxDB as a Database in a Vue.js Application","docId":"articles/vue-database","unlisted":false},{"type":"link","href":"/articles/vue-indexeddb.html","label":"IndexedDB Database in Vue Apps - The Power of RxDB","docId":"articles/vue-indexeddb","unlisted":false},{"type":"link","href":"/articles/jquery-database.html","label":"RxDB as a Database in a jQuery Application","docId":"articles/jquery-database","unlisted":false},{"type":"link","href":"/articles/firestore-alternative.html","label":"RxDB - Firestore Alternative to Sync with Your Own Backend","docId":"articles/firestore-alternative","unlisted":false},{"type":"link","href":"/articles/firebase-realtime-database-alternative.html","label":"RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend","docId":"articles/firebase-realtime-database-alternative","unlisted":false},{"type":"link","href":"/articles/offline-database.html","label":"RxDB \u2013 The Ultimate Offline Database with Sync and Encryption","docId":"articles/offline-database","unlisted":false},{"type":"link","href":"/articles/zero-latency-local-first.html","label":"Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression","docId":"articles/zero-latency-local-first","unlisted":false},{"type":"link","href":"/articles/indexeddb-max-storage-limit.html","label":"IndexedDB Max Storage Size Limit - Detailed Best Practices","docId":"articles/indexeddb-max-storage-limit","unlisted":false},{"type":"link","href":"/articles/json-based-database.html","label":"JSON-Based Databases - Why NoSQL and RxDB Simplify App Development","docId":"articles/json-based-database","unlisted":false},{"type":"link","href":"/articles/reactjs-storage.html","label":"ReactJS Storage - From Basic LocalStorage to Advanced Offline Apps with RxDB","docId":"articles/reactjs-storage","unlisted":false}],"collapsed":true,"collapsible":true},{"type":"link","href":"/contribution.html","label":"Contribute","docId":"contribute","unlisted":false},{"type":"category","label":"Contact","items":[{"type":"link","label":"Consulting","href":"/consulting/"},{"type":"link","label":"Discord","href":"/chat/","customProps":{"target":"_blank"}},{"type":"link","label":"LinkedIn","href":"https://www.linkedin.com/company/rxdb/"}],"collapsed":true,"collapsible":true}]},"docs":{"adapters":{"id":"adapters","title":"PouchDB Adapters","description":"When you use PouchDB RxStorage, there are many adapters that define where the data has to be stored."},"alternatives":{"id":"alternatives","title":"Alternatives for realtime local-first JavaScript applications and local databases","description":"Explore real-time, local-first JS alternatives to RxDB. Compare Firebase, Meteor, AWS, CouchDB, and more for robust, seamless web/mobile app development.","sidebar":"tutorialSidebar"},"articles/angular-database":{"id":"articles/angular-database","title":"RxDB as a Database in an Angular Application","description":"Level up your Angular projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.","sidebar":"tutorialSidebar"},"articles/angular-indexeddb":{"id":"articles/angular-indexeddb","title":"Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone","description":"Discover how to harness IndexedDB in Angular with RxDB for robust offline apps. Learn reactive queries, advanced features, and more.","sidebar":"tutorialSidebar"},"articles/browser-database":{"id":"articles/browser-database","title":"Benefits of RxDB & Browser Databases","description":"Find out why RxDB is the go-to solution for browser databases. See how it boosts performance, simplifies replication, and powers real-time UIs.","sidebar":"tutorialSidebar"},"articles/browser-storage":{"id":"articles/browser-storage","title":"Browser Storage - RxDB as a Database for Browsers","description":"Explore RxDB for browser storage its advantages, limitations, and why it outperforms SQL databases in web applications for enhanced efficiency","sidebar":"tutorialSidebar"},"articles/data-base":{"id":"articles/data-base","title":"Empower Web Apps with Reactive RxDB Data-base","description":"Explore RxDB\'s reactive data-base solution for web and mobile. Enable offline-first experiences, real-time syncing, and secure data handling with ease.","sidebar":"tutorialSidebar"},"articles/embedded-database":{"id":"articles/embedded-database","title":"Embedded Database, Real-time Speed - RxDB","description":"Unleash the power of embedded databases with RxDB. Explore real-time replication, offline access, and reactive queries for modern JavaScript apps.","sidebar":"tutorialSidebar"},"articles/firebase-realtime-database-alternative":{"id":"articles/firebase-realtime-database-alternative","title":"RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend","description":"Looking for a Firebase Realtime Database alternative? RxDB offers a fully offline, vendor-agnostic NoSQL solution with advanced conflict resolution and multi-platform support.","sidebar":"tutorialSidebar"},"articles/firestore-alternative":{"id":"articles/firestore-alternative","title":"RxDB - Firestore Alternative to Sync with Your Own Backend","description":"Looking for a Firestore alternative? RxDB is a local-first, NoSQL database that syncs seamlessly with any backend, offers rich offline capabilities, advanced conflict resolution, and reduces vendor lock-in.","sidebar":"tutorialSidebar"},"articles/flutter-database":{"id":"articles/flutter-database","title":"Supercharge Flutter Apps with the RxDB Database","description":"Harness RxDB\'s reactive database to bring real-time, offline-first data storage and syncing to your next Flutter application.","sidebar":"tutorialSidebar"},"articles/frontend-database":{"id":"articles/frontend-database","title":"RxDB - The Ultimate JS Frontend Database","description":"Discover how RxDB, a powerful JavaScript frontend database, boosts offline access, caching, and real-time updates to supercharge your web apps.","sidebar":"tutorialSidebar"},"articles/ideas":{"id":"articles/ideas","title":"ideas for articles","description":"- storing and searching through 1mio emails in a browser database"},"articles/in-memory-nosql-database":{"id":"articles/in-memory-nosql-database","title":"RxDB In-Memory NoSQL - Supercharge Real-Time Apps","description":"Discover how RxDB\'s in-memory NoSQL engine delivers blazing speed for real-time apps, ensuring responsive experiences and seamless data sync.","sidebar":"tutorialSidebar"},"articles/indexeddb-max-storage-limit":{"id":"articles/indexeddb-max-storage-limit","title":"IndexedDB Max Storage Size Limit - Detailed Best Practices","description":"Learn how browsers enforce IndexedDB storage size limits, how to test and handle quota exceeded errors, and best practices for storing large amounts of data offline.","sidebar":"tutorialSidebar"},"articles/ionic-database":{"id":"articles/ionic-database","title":"RxDB - The Perfect Ionic Database","description":"Supercharge your Ionic hybrid apps with RxDB\'s offline-first database. Experience real-time sync, top performance, and easy replication.","sidebar":"tutorialSidebar"},"articles/ionic-storage":{"id":"articles/ionic-storage","title":"RxDB - Local Ionic Storage with Encryption, Compression & Sync","description":"The best Ionic storage solution? RxDB empowers your hybrid apps with offline-first capabilities, secure encryption, data compression, and seamless data syncing to any backend.","sidebar":"tutorialSidebar"},"articles/javascript-vector-database":{"id":"articles/javascript-vector-database","title":"Local JavaScript Vector Database that works offline","description":"Create a blazing-fast vector database in JavaScript. Leverage RxDB and transformers.js for instant, offline semantic search - no servers required!","sidebar":"tutorialSidebar"},"articles/jquery-database":{"id":"articles/jquery-database","title":"RxDB as a Database in a jQuery Application","description":"Level up your jQuery-based projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.","sidebar":"tutorialSidebar"},"articles/json-based-database":{"id":"articles/json-based-database","title":"JSON-Based Databases - Why NoSQL and RxDB Simplify App Development","description":"Dive into how JSON-based databases power modern UI-centric apps, why NoSQL often outperforms SQL for dynamic data, how SQLite accommodates JSON, and why RxDB delivers a seamless offline-first solution for JavaScript with advanced JSON features.","sidebar":"tutorialSidebar"},"articles/json-database":{"id":"articles/json-database","title":"RxDB - The JSON Database Built for JavaScript","description":"Experience a powerful JSON database with RxDB, built for JavaScript. Store, sync, and compress your data seamlessly across web and mobile apps.","sidebar":"tutorialSidebar"},"articles/local-database":{"id":"articles/local-database","title":"What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications","description":"An in-depth exploration of local databases and why RxDB excels as a local-first solution for JavaScript applications.","sidebar":"tutorialSidebar"},"articles/local-first-future":{"id":"articles/local-first-future","title":"Why Local-First Software Is the Future and its Limitations","description":"Discover how local-first transforms web apps, boosts offline resilience, and why instant user feedback is becoming the new normal.","sidebar":"tutorialSidebar"},"articles/localstorage":{"id":"articles/localstorage","title":"Using localStorage in Modern Applications - A Comprehensive Guide","description":"This guide explores localStorage in JavaScript web apps, detailing its usage, limitations, and alternatives like IndexedDB and AsyncStorage.","sidebar":"tutorialSidebar"},"articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm":{"id":"articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm","title":"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite","description":"Compare LocalStorage, IndexedDB, Cookies, OPFS, and WASM-SQLite for web storage, performance, limits, and best practices for modern web apps.","sidebar":"tutorialSidebar"},"articles/mobile-database":{"id":"articles/mobile-database","title":"Real-Time & Offline - RxDB for Mobile Apps","description":"Explore RxDB as your reliable mobile database. Enjoy offline-first capabilities, real-time sync, and seamless integration for hybrid app development.","sidebar":"tutorialSidebar"},"articles/offline-database":{"id":"articles/offline-database","title":"RxDB \u2013 The Ultimate Offline Database with Sync and Encryption","description":"Discover how RxDB serves as a powerful offline database, offering real-time synchronization, secure encryption, and an offline-first approach for modern web and mobile apps.","sidebar":"tutorialSidebar"},"articles/optimistic-ui":{"id":"articles/optimistic-ui","title":"Building an Optimistic UI with RxDB","description":"Learn how to build an Optimistic UI with RxDB for instant and reliable UI updates on user interactions","sidebar":"tutorialSidebar"},"articles/progressive-web-app-database":{"id":"articles/progressive-web-app-database","title":"RxDB as a Database for Progressive Web Apps (PWA)","description":"Discover how RxDB supercharges Progressive Web Apps with real-time sync, offline-first capabilities, and lightning-fast data handling.","sidebar":"tutorialSidebar"},"articles/react-database":{"id":"articles/react-database","title":"RxDB as a Database for React Applications","description":"earn how the RxDB database supercharges React apps with offline access, real-time updates, and smooth data flow. Boost performance and engagement.","sidebar":"tutorialSidebar"},"articles/react-indexeddb":{"id":"articles/react-indexeddb","title":"IndexedDB Database in React Apps - The Power of RxDB","description":"Discover how RxDB simplifies IndexedDB in React, offering reactive queries, offline-first capability, encryption, compression, and effortless integration.","sidebar":"tutorialSidebar"},"articles/react-native-encryption":{"id":"articles/react-native-encryption","title":"React Native Encryption and Encrypted Database/Storage","description":"Secure your React Native app with RxDB encryption. Learn why it matters, how to implement encrypted databases, and best practices to protect user data.","sidebar":"tutorialSidebar"},"articles/reactjs-storage":{"id":"articles/reactjs-storage","title":"ReactJS Storage - From Basic LocalStorage to Advanced Offline Apps with RxDB","description":"Discover how to implement reactjs storage using localStorage for quick key-value data, then move on to more robust offline-first approaches with RxDB, IndexedDB, preact signals, encryption plugins, and more.","sidebar":"tutorialSidebar"},"articles/realtime-database":{"id":"articles/realtime-database","title":"What Really Is a Realtime Database?","description":"Discover how RxDB merges realtime replication and dynamic updates to deliver seamless data sync across browsers, devices, and servers - instantly.","sidebar":"tutorialSidebar"},"articles/vue-database":{"id":"articles/vue-database","title":"RxDB as a Database in a Vue.js Application","description":"Level up your Vue projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.","sidebar":"tutorialSidebar"},"articles/vue-indexeddb":{"id":"articles/vue-indexeddb","title":"IndexedDB Database in Vue Apps - The Power of RxDB","description":"Learn how RxDB simplifies IndexedDB in Vue, offering reactive queries, offline-first capabilities, encryption, compression, and effortless integration.","sidebar":"tutorialSidebar"},"articles/websockets-sse-polling-webrtc-webtransport":{"id":"articles/websockets-sse-polling-webrtc-webtransport","title":"WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport","description":"Learn the unique benefits and pitfalls of each real-time tech. Make informed decisions on WebSockets, SSE, Polling, WebRTC, and WebTransport.","sidebar":"tutorialSidebar"},"articles/zero-latency-local-first":{"id":"articles/zero-latency-local-first","title":"Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression","description":"Build blazing-fast, zero-latency local first apps with RxDB. Gain instant UI responses, robust offline capabilities, end-to-end encryption, and data compression for streamlined performance.","sidebar":"tutorialSidebar"},"backup":{"id":"backup","title":"Backup","description":"Easily back up your RxDB database to JSON files and attachments on the filesystem with the Backup Plugin - ensuring reliable Node.js data protection.","sidebar":"tutorialSidebar"},"capacitor-database":{"id":"capacitor-database","title":"Capacitor Database Guide - SQLite, RxDB & More","description":"Explore Capacitor\'s top data storage solutions - from key-value to real-time databases. Compare SQLite, RxDB, and more in this in-depth guide.","sidebar":"tutorialSidebar"},"cleanup":{"id":"cleanup","title":"Cleanup","description":"Optimize storage and speed up queries with RxDB\'s Cleanup Plugin, automatically removing old deleted docs while preserving replication states.","sidebar":"tutorialSidebar"},"contribute":{"id":"contribute","title":"Contribute","description":"Got a fix or fresh idea? Learn how to contribute to RxDB, run tests, and shape the future of this cutting-edge NoSQL database for JavaScript.","sidebar":"tutorialSidebar"},"crdt":{"id":"crdt","title":"CRDT - Conflict-free replicated data type Database","description":"Learn how RxDB\'s CRDT Plugin resolves document conflicts automatically in distributed systems, ensuring seamless merges and consistent data.","sidebar":"tutorialSidebar"},"data-migration":{"id":"data-migration","title":"Data Migration","description":"This documentation page has been moved to here"},"dev-mode":{"id":"dev-mode","title":"Development Mode","description":"Enable checks & validations with RxDB Dev Mode. Ensure proper API use, readable errors, and schema validation during development. Avoid in production.","sidebar":"tutorialSidebar"},"downsides-of-offline-first":{"id":"downsides-of-offline-first","title":"Downsides of Local First / Offline First","description":"Discover the hidden pitfalls of local-first apps. Learn about storage limits, conflicts, and real-time illusions before building your offline solution.","sidebar":"tutorialSidebar"},"electron":{"id":"electron","title":"Seamless Electron Storage with RxDB","description":"Use the RxDB Electron Plugin to share data between main and renderer processes. Enjoy quick queries, real-time sync, and robust offline support.","sidebar":"tutorialSidebar"},"electron-database":{"id":"electron-database","title":"Electron Database - Storage adapters for SQLite, Filesystem and In-Memory","description":"Harness the database power of SQLite, Filesystem, and in-memory storage in Electron with RxDB. Build fast, offline-first apps that sync in real time.","sidebar":"tutorialSidebar"},"encryption":{"id":"encryption","title":"Encryption","description":"Explore RxDB\'s \ud83d\udd12 encryption plugin for enhanced data security in web and native apps, featuring password-based encryption and secure storage.","sidebar":"tutorialSidebar"},"errors":{"id":"errors","title":"Error Messages","description":"Learn how RxDB throws RxErrors with codes and parameters. Keep builds lean, yet unveil full messages in development via the DevMode plugin.","sidebar":"tutorialSidebar"},"fulltext-search":{"id":"fulltext-search","title":"Fulltext Search \ud83d\udc51","description":"Master local fulltext search with RxDB\'s FlexSearch plugin. Enjoy real-time indexing, efficient queries, and offline-first support made easy.","sidebar":"tutorialSidebar"},"install":{"id":"install","title":"Installation","description":"Learn how to install RxDB via npm, configure polyfills, and fix global variable errors in Angular or Webpack for a seamless setup.","sidebar":"tutorialSidebar"},"key-compression":{"id":"key-compression","title":"Key Compression","description":"With the key compression plugin, documents will be stored in a compressed format which saves up to 40% disc space.","sidebar":"tutorialSidebar"},"leader-election":{"id":"leader-election","title":"Leader Election","description":"RxDB comes with a leader-election which elects a leading instance between different instances in the same javascript runtime.","sidebar":"tutorialSidebar"},"logger":{"id":"logger","title":"RxDB Logger Plugin - Track & Optimize","description":"Take control of your RxDatabase logs. Monitor every write, query, or attachment retrieval to swiftly diagnose and fix performance bottlenecks.","sidebar":"tutorialSidebar"},"middleware":{"id":"middleware","title":"Streamlined RxDB Middleware","description":"Enhance your RxDB workflow with pre and post hooks. Quickly add custom validations, triggers, and events to streamline your asynchronous operations.","sidebar":"tutorialSidebar"},"migration-schema":{"id":"migration-schema","title":"Seamless Schema Data Migration with RxDB","description":"Upgrade your RxDB collections without losing data. Learn how to seamlessly migrate schema changes and keep your apps running smoothly.","sidebar":"tutorialSidebar"},"migration-storage":{"id":"migration-storage","title":"Migration Storage","description":"Effortlessly migrate your data between storages in RxDB using the Storage Migration plugin. Retain your documents when switching storages or major versions.","sidebar":"tutorialSidebar"},"nodejs-database":{"id":"nodejs-database","title":"RxDB - The Real-Time Database for Node.js","description":"Discover how RxDB brings flexible, reactive NoSQL to Node.js. Scale effortlessly, persist data, and power your server-side apps with ease.","sidebar":"tutorialSidebar"},"nosql-performance-tips":{"id":"nosql-performance-tips","title":"RxDB NoSQL Performance Tips","description":"Skyrocket your NoSQL speed with RxDB tips. Learn about bulk writes, optimized queries, and lean plugin usage for peak performance.","sidebar":"tutorialSidebar"},"offline-first":{"id":"offline-first","title":"Local First / Offline First","description":"Local-First software stores data on client devices for seamless offline and online functionality, enhancing user experience and efficiency.","sidebar":"tutorialSidebar"},"orm":{"id":"orm","title":"ORM","description":"Like mongoose, RxDB has ORM-capabilities which can be used to add specific behavior to documents and collections.","sidebar":"tutorialSidebar"},"overview":{"id":"overview","title":"RxDB Docs","description":"RxDB Documentation Overview","sidebar":"tutorialSidebar"},"plugins":{"id":"plugins","title":"Creating Plugins","description":"Creating your own plugin is very simple. A plugin is basically a javascript-object which overwrites or extends RxDB\'s internal classes, prototypes, and hooks.","sidebar":"tutorialSidebar"},"population":{"id":"population","title":"Populate and Link Docs in RxDB","description":"Learn how to reference and link documents across collections in RxDB. Discover easy population without joins and handle complex relationships.","sidebar":"tutorialSidebar"},"query-cache":{"id":"query-cache","title":"Efficient RxDB Queries via Query Cache","description":"Learn how RxDB\'s Query Cache boosts performance by reusing queries. Discover its default replacement policy and how to define your own.","sidebar":"tutorialSidebar"},"query-optimizer":{"id":"query-optimizer","title":"Optimize Client-Side Queries with RxDB","description":"Harness real-world data to fine-tune queries. The build-time RxDB Optimizer finds the perfect index, boosting query speed in any environment.","sidebar":"tutorialSidebar"},"quickstart":{"id":"quickstart","title":"\ud83d\ude80 Quickstart","description":"Learn how to build a realtime app with RxDB. Follow this quickstart for setup, schema creation, data operations, and real-time syncing.","sidebar":"tutorialSidebar"},"react":{"id":"react","title":"React","description":"RxDB provides first-class support for both React and React Native via a dedicated React integration. This integration makes it possible to use RxDB inside functional components using React Context and hooks, without manually subscribing to observables or managing cleanup logic.","sidebar":"tutorialSidebar"},"react-native-database":{"id":"react-native-database","title":"React Native Database - Sync & Store Like a Pro","description":"Discover top React Native local database solutions - AsyncStorage, SQLite, RxDB, and more. Build offline-ready apps for iOS, Android, and Windows with easy sync.","sidebar":"tutorialSidebar"},"reactivity":{"id":"reactivity","title":"Signals & Custom Reactivity with RxDB","description":"Level up reactivity with Angular signals, Vue refs, or Preact signals in RxDB. Learn how to integrate custom reactivity to power your dynamic UI.","sidebar":"tutorialSidebar"},"releases/10.0.0":{"id":"releases/10.0.0","title":"RxDB 10.0.0 - Built for the Future","description":"Experience faster, future-proof data handling in RxDB 10.0. Explore new storage interfaces, composite keys, and major performance upgrades.","sidebar":"tutorialSidebar"},"releases/11.0.0":{"id":"releases/11.0.0","title":"RxDB 11 - WebWorker Support & More","description":"RxDB 11.0 brings Worker-based storage for multi-core performance gains. Explore the major changes and migrate seamlessly to harness new speeds.","sidebar":"tutorialSidebar"},"releases/12.0.0":{"id":"releases/12.0.0","title":"RxDB 12.0.0 - Clean, Lean & Mean","description":"Upgrade to RxDB 12.0.0 for blazing-fast queries, streamlined replication, and better index usage. Explore new features and power up your app.","sidebar":"tutorialSidebar"},"releases/13.0.0":{"id":"releases/13.0.0","title":"RxDB 13.0.0 - A New Era of Replication","description":"Discover RxDB 13.0\'s brand-new replication protocol, faster performance, and real-time collaboration for a seamless offline-first experience.","sidebar":"tutorialSidebar"},"releases/14.0.0":{"id":"releases/14.0.0","title":"RxDB 14.0 - Major Changes & New Features","description":"Discover RxDB 14.0, a major release packed with API changes, improved performance, and streamlined storage, ensuring faster data operations than ever.","sidebar":"tutorialSidebar"},"releases/15.0.0":{"id":"releases/15.0.0","title":"RxDB 15.0.0 - Major Migration Overhaul","description":"Discover RxDB 15.0.0, featuring new migration strategies, replication improvements, and lightning-fast performance to supercharge your app.","sidebar":"tutorialSidebar"},"releases/16.0.0":{"id":"releases/16.0.0","title":"RxDB 16.0.0 - Efficiency Redefined","description":"Meet RxDB 16.0.0 - major refactorings, improved performance, and easy migrations. Experience a better, faster, and more stable database solution today.","sidebar":"tutorialSidebar"},"releases/17.0.0":{"id":"releases/17.0.0","title":"RxDB 17.0.0","description":"RxDB 17 introduces improved reactivity APIs, better debugging, breaking storage fixes, and multiple plugins graduating from beta.","sidebar":"tutorialSidebar"},"releases/8.0.0":{"id":"releases/8.0.0","title":"Meet RxDB 8.0.0 - New Defaults & Performance","description":"Discover how RxDB 8.0.0 boosts performance, simplifies schema handling, and streamlines reactive data updates for modern apps.","sidebar":"tutorialSidebar"},"releases/9.0.0":{"id":"releases/9.0.0","title":"RxDB 9.0.0 - Faster & Simpler","description":"Discover RxDB 9.0.0\'s streamlined plugins, top-level schema definitions, and improved dev-mode. Experience a simpler, faster real-time database.","sidebar":"tutorialSidebar"},"replication":{"id":"replication","title":"\u2699\ufe0f RxDB realtime Sync Engine for Local-First Apps","description":"Replicate data in real-time with RxDB\'s offline-first Sync Engine. Learn about efficient syncing, conflict resolution, and advanced multi-tab support.","sidebar":"tutorialSidebar"},"replication-appwrite":{"id":"replication-appwrite","title":"Appwrite Realtime Sync for Local-First Apps","description":"Sync RxDB with Appwrite for local-first apps. Supports real-time updates, offline mode, conflict resolution, and secure push/pull replication.","sidebar":"tutorialSidebar"},"replication-couchdb":{"id":"replication-couchdb","title":"RxDB\'s CouchDB Replication Plugin","description":"Replicate your RxDB collections with CouchDB the fast way. Enjoy faster sync, easier conflict handling, and flexible storage using this modern plugin.","sidebar":"tutorialSidebar"},"replication-firestore":{"id":"replication-firestore","title":"Smooth Firestore Sync for Offline Apps","description":"Leverage RxDB to enable real-time, offline-first replication with Firestore. Cut cloud costs, resolve conflicts, and speed up your app.","sidebar":"tutorialSidebar"},"replication-graphql":{"id":"replication-graphql","title":"GraphQL Replication","description":"The GraphQL replication provides handlers for GraphQL to run replication with GraphQL as the transportation layer.","sidebar":"tutorialSidebar"},"replication-http":{"id":"replication-http","title":"HTTP Replication","description":"Learn how to establish HTTP replication between RxDB clients and a Node.js Express server for data synchronization.","sidebar":"tutorialSidebar"},"replication-mongodb":{"id":"replication-mongodb","title":"MongoDB Realtime Sync Engine for Local-First Apps","description":"Build real-time, offline-capable apps with RxDB + MongoDB replication. Push/pull changes, use change streams, and keep data in sync across devices.","sidebar":"tutorialSidebar"},"replication-nats":{"id":"replication-nats","title":"RxDB & NATS - Realtime Sync","description":"Seamlessly sync your RxDB data with NATS for real-time, two-way replication. Handle conflicts, errors, and retries with ease.","sidebar":"tutorialSidebar"},"replication-p2p":{"id":"replication-p2p","title":"Seamless P2P Data Sync","description":"Discover how replication-webrtc ensures secure P2P data sync and real-time collaboration with RxDB. Explore the future of offline-first apps!"},"replication-server":{"id":"replication-server","title":"RxDB Server Replication","description":"The Server Replication Plugin connects to the replication endpoint of an RxDB Server Replication Endpoint and replicates data between the client and the server.","sidebar":"tutorialSidebar"},"replication-supabase":{"id":"replication-supabase","title":"Supabase Replication Plugin for RxDB - Real-Time, Offline-First Sync","description":"Build real-time, offline-capable apps with RxDB + Supabase. Push/pull changes via PostgREST, stream updates with Realtime, and keep data in sync across devices.","sidebar":"tutorialSidebar"},"replication-webrtc":{"id":"replication-webrtc","title":"WebRTC P2P Replication with RxDB - Sync Browsers and Devices","description":"Learn to set up peer-to-peer WebRTC replication with RxDB. Bypass central servers and enjoy secure, low-latency data sync across all clients.","sidebar":"tutorialSidebar"},"replication-websocket":{"id":"replication-websocket","title":"Websocket Replication","description":"With the websocket replication plugin, you can spawn a websocket server from a RxDB database in Node.js and replicate with it.","sidebar":"tutorialSidebar"},"rx-attachment":{"id":"rx-attachment","title":"Attachments","description":"Learn how to store and manage binary data like images or files in RxDB using attachments. Discover features, performance benefits, encryption, compression, and usage examples with code.","sidebar":"tutorialSidebar"},"rx-collection":{"id":"rx-collection","title":"Master Data - Create and Manage RxCollections","description":"Discover how to create, manage, and migrate documents in RxCollections. Harness real-time data flows, secure encryption, and powerful performance in RxDB.","sidebar":"tutorialSidebar"},"rx-database":{"id":"rx-database","title":"RxDatabase - The Core of Your Realtime Data","description":"Get started with RxDatabase and integrate multiple storages. Learn to create, encrypt, and optimize your realtime database today.","sidebar":"tutorialSidebar"},"rx-document":{"id":"rx-document","title":"RxDocument","description":"Master RxDB\'s RxDocument - Insert, find, update, remove, and more for streamlined data handling in modern apps.","sidebar":"tutorialSidebar"},"rx-local-document":{"id":"rx-local-document","title":"Master Local Documents in RxDB","description":"Effortlessly store custom metadata and app settings in RxDB. Learn how Local Documents keep data flexible, secure, and easy to manage.","sidebar":"tutorialSidebar"},"rx-pipeline":{"id":"rx-pipeline","title":"RxPipeline - Automate Data Flows in RxDB","description":"Discover how RxPipeline automates your data workflows. Seamlessly process writes, manage leader election, and ensure crash-safe operations in RxDB.","sidebar":"tutorialSidebar"},"rx-query":{"id":"rx-query","title":"RxQuery","description":"Master RxQuery in RxDB - find, update, remove documents using Mango syntax, chained queries, real-time observations, indexing, and more.","sidebar":"tutorialSidebar"},"rx-schema":{"id":"rx-schema","title":"Design Perfect Schemas in RxDB","description":"Learn how to define, secure, and validate your data in RxDB. Master primary keys, indexes, encryption, and more with the RxSchema approach.","sidebar":"tutorialSidebar"},"rx-server":{"id":"rx-server","title":"RxDB Server - Deploy Your Data","description":"Launch a secure, high-performance server on top of your RxDB database. Enable REST, replication endpoints, and seamless data syncing with RxServer.","sidebar":"tutorialSidebar"},"rx-server-scaling":{"id":"rx-server-scaling","title":"RxServer Scaling - Vertical or Horizontal","description":"Discover vertical and horizontal techniques to boost RxServer. Learn multiple processes, worker threads, and replication for limitless performance.","sidebar":"tutorialSidebar"},"rx-state":{"id":"rx-state","title":"RxState - Reactive Persistent State with RxDB","description":"Get real-time, persistent state without the hassle. RxState integrates easily with signals and hooks, ensuring smooth updates across tabs and devices.","sidebar":"tutorialSidebar"},"rx-storage":{"id":"rx-storage","title":"\u2699\ufe0f RxStorage Layer - Choose the Perfect RxDB Storage for Every Use Case","description":"Discover how RxDB\'s modular RxStorage lets you swap engines and unlock top performance, no matter the environment or use case.","sidebar":"tutorialSidebar"},"rx-storage-denokv":{"id":"rx-storage-denokv","title":"DenoKV RxStorage","description":"With the DenoKV RxStorage layer for RxDB, you can run a fully featured NoSQL database on top of the DenoKV API.","sidebar":"tutorialSidebar"},"rx-storage-dexie":{"id":"rx-storage-dexie","title":"RxDB Dexie.js Database - Fast, Reactive, Sync with Any Backend","description":"Use Dexie.js to power RxDB in the browser. Enjoy quick setup, Dexie addons, and reliable storage for small apps or prototypes.","sidebar":"tutorialSidebar"},"rx-storage-filesystem-node":{"id":"rx-storage-filesystem-node","title":"Blazing-Fast Node Filesystem Storage","description":"Get up and running quickly with RxDB\'s Filesystem Node RxStorage. Store data in JSON, embrace multi-instance support, and enjoy a simpler database.","sidebar":"tutorialSidebar"},"rx-storage-foundationdb":{"id":"rx-storage-foundationdb","title":"RxDB on FoundationDB - Performance at Scale","description":"Combine FoundationDB\'s reliability with RxDB\'s indexing and schema validation. Build scalable apps with faster queries and real-time data.","sidebar":"tutorialSidebar"},"rx-storage-indexeddb":{"id":"rx-storage-indexeddb","title":"Instant Performance with IndexedDB RxStorage","description":"Choose IndexedDB RxStorage for unmatched speed and minimal build size. Perfect for fast-performing apps that demand reliable, lightweight data solutions.","sidebar":"tutorialSidebar"},"rx-storage-localstorage":{"id":"rx-storage-localstorage","title":"RxDB LocalStorage - The Easiest Way to Persist Data in Your Web App","description":"Discover how to quickly set up RxDB\'s LocalStorage-based storage as the recommended default. Learn its benefits, limitations, and why it\u2019s perfect for demos, prototypes, and lightweight applications.","sidebar":"tutorialSidebar"},"rx-storage-localstorage-meta-optimizer":{"id":"rx-storage-localstorage-meta-optimizer","title":"Fastest RxDB Starts - Localstorage Meta Optimizer","description":"Wrap any RxStorage with localStorage metadata to slash initial load by up to 200ms. Unlock speed with this must-have RxDB Premium plugin.","sidebar":"tutorialSidebar"},"rx-storage-lokijs":{"id":"rx-storage-lokijs","title":"Empower RxDB with the LokiJS RxStorage","description":"Discover the lightning-fast LokiJS RxStorage for RxDB. Explore in-memory speed, multi-tab support, and pros & cons of this unique storage solution."},"rx-storage-memory":{"id":"rx-storage-memory","title":"Lightning-Fast Memory Storage for RxDB","description":"Use Memory RxStorage for a high-performance, JavaScript in-memory database. Built for speed, making it perfect for unit tests and rapid prototyping.","sidebar":"tutorialSidebar"},"rx-storage-memory-mapped":{"id":"rx-storage-memory-mapped","title":"Blazing-Fast Memory Mapped RxStorage","description":"Boost your app\'s performance with Memory Mapped RxStorage. Query and write in-memory while seamlessly persisting data to your chosen storage.","sidebar":"tutorialSidebar"},"rx-storage-memory-synced":{"id":"rx-storage-memory-synced","title":"Instant Performance with Memory Synced RxStorage","description":"Accelerate RxDB with in-memory storage replicated to disk. Enjoy instant queries, faster loads, and unstoppable performance for your web apps."},"rx-storage-mongodb":{"id":"rx-storage-mongodb","title":"Unlock MongoDB Power with RxDB","description":"Combine RxDB\'s real-time sync with MongoDB\'s scalability. Harness the MongoDB RxStorage to seamlessly expand your database capabilities.","sidebar":"tutorialSidebar"},"rx-storage-opfs":{"id":"rx-storage-opfs","title":"Supercharged OPFS Database with RxDB","description":"Discover how to harness the Origin Private File System with RxDB\'s OPFS RxStorage for unrivaled performance and security in client-side data storage.","sidebar":"tutorialSidebar"},"rx-storage-performance":{"id":"rx-storage-performance","title":"\ud83d\udcc8 Discover RxDB Storage Benchmarks","description":"Explore real-world benchmarks comparing RxDB\'s persistent and semi-persistent storages. Discover which storage option delivers the fastest performance.","sidebar":"tutorialSidebar"},"rx-storage-pouchdb":{"id":"rx-storage-pouchdb","title":"PouchDB RxStorage - Migrate for Better Performance","description":"Discover why PouchDB RxStorage is deprecated in RxDB. Learn its legacy, performance drawbacks, and how to upgrade to a faster solution."},"rx-storage-remote":{"id":"rx-storage-remote","title":"Remote RxStorage","description":"The Remote RxStorage is made to use a remote storage and communicate with it over an asynchronous message channel.","sidebar":"tutorialSidebar"},"rx-storage-sharding":{"id":"rx-storage-sharding","title":"Sharding RxStorage \ud83d\udc51","description":"With the sharding plugin, you can improve the write and query times of some RxStorage implementations.","sidebar":"tutorialSidebar"},"rx-storage-shared-worker":{"id":"rx-storage-shared-worker","title":"Boost Performance with SharedWorker RxStorage","description":"Tap into single-instance storage with RxDB\'s SharedWorker. Improve efficiency, cut duplication, and keep your app lightning-fast across tabs.","sidebar":"tutorialSidebar"},"rx-storage-sqlite":{"id":"rx-storage-sqlite","title":"RxDB SQLite RxStorage for Hybrid Apps","description":"Unlock seamless persistence with SQLite RxStorage. Explore usage in hybrid apps, compare performance, and leverage advanced features like attachments.","sidebar":"tutorialSidebar"},"rx-storage-worker":{"id":"rx-storage-worker","title":"Turbocharge RxDB with Worker RxStorage","description":"Offload RxDB queries to WebWorkers or Worker Threads, freeing the main thread and boosting performance. Experience smoother apps with Worker RxStorage.","sidebar":"tutorialSidebar"},"rxdb-tradeoffs":{"id":"rxdb-tradeoffs","title":"RxDB Tradeoffs - Why NoSQL Triumphs on the Client","description":"Uncover RxDB\'s approach to modern database needs. From JSON-based queries to conflict handling without transactions, learn RxDB\'s unique tradeoffs."},"schema-validation":{"id":"schema-validation","title":"Schema Validation","description":"RxDB has multiple validation implementations that can be used to ensure that your document data is always matching the provided JSON","sidebar":"tutorialSidebar"},"slow-indexeddb":{"id":"slow-indexeddb","title":"Solving IndexedDB Slowness for Seamless Apps","description":"Struggling with IndexedDB performance? Discover hidden bottlenecks, advanced tuning techniques, and next-gen storage like the File System Access API.","sidebar":"tutorialSidebar"},"third-party-plugins":{"id":"third-party-plugins","title":"Boost Your RxDB with Powerful Third-Party Plugins","description":"Unleash RxDB\'s full power! Explore third-party plugins for advanced hooks, text search, Laravel Orion, Supabase replication, and more.","sidebar":"tutorialSidebar"},"transactions-conflicts-revisions":{"id":"transactions-conflicts-revisions","title":"Transactions, Conflicts and Revisions","description":"Learn RxDB\'s approach to local and replication conflicts. Discover how incremental writes and custom handlers keep your app stable and efficient.","sidebar":"tutorialSidebar"},"tutorials/typescript":{"id":"tutorials/typescript","title":"TypeScript Setup","description":"Use RxDB with TypeScript to define typed schemas, create typed collections, and build fully typed ORM methods. A quick step-by-step guide.","sidebar":"tutorialSidebar"},"why-nosql":{"id":"why-nosql","title":"Why NoSQL Powers Modern UI Apps","description":"Discover how NoSQL enables offline-first UI apps. Learn about easy replication, conflict resolution, and why relational data isn\'t always necessary.","sidebar":"tutorialSidebar"}}}}')}}]);
\ No newline at end of file
diff --git a/docs/assets/js/2456d5e0.565789dc.js b/docs/assets/js/2456d5e0.565789dc.js
deleted file mode 100644
index 93e0181e3ec..00000000000
--- a/docs/assets/js/2456d5e0.565789dc.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2966],{7615(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>i,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"rx-database","title":"RxDatabase - The Core of Your Realtime Data","description":"Get started with RxDatabase and integrate multiple storages. Learn to create, encrypt, and optimize your realtime database today.","source":"@site/docs/rx-database.md","sourceDirName":".","slug":"/rx-database.html","permalink":"/rx-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDatabase - The Core of Your Realtime Data","slug":"rx-database.html","description":"Get started with RxDatabase and integrate multiple storages. Learn to create, encrypt, and optimize your realtime database today."},"sidebar":"tutorialSidebar","previous":{"title":"TypeScript Setup","permalink":"/tutorials/typescript.html"},"next":{"title":"RxSchema","permalink":"/rx-schema.html"}}');var a=n(4848),o=n(8453);const i={title:"RxDatabase - The Core of Your Realtime Data",slug:"rx-database.html",description:"Get started with RxDatabase and integrate multiple storages. Learn to create, encrypt, and optimize your realtime database today."},t="RxDatabase",l={},c=[{value:"Creation",id:"creation",level:2},{value:"name",id:"name",level:3},{value:"storage",id:"storage",level:3},{value:"password",id:"password",level:3},{value:"multiInstance",id:"multiinstance",level:3},{value:"eventReduce",id:"eventreduce",level:3},{value:"ignoreDuplicate",id:"ignoreduplicate",level:3},{value:"closeDuplicates",id:"closeduplicates",level:3},{value:"hashFunction",id:"hashfunction",level:3},{value:"Methods",id:"methods",level:2},{value:"Observe with $",id:"observe-with-",level:3},{value:"exportJSON()",id:"exportjson",level:3},{value:"importJSON()",id:"importjson",level:3},{value:"backup()",id:"backup",level:3},{value:"waitForLeadership()",id:"waitforleadership",level:3},{value:"requestIdlePromise()",id:"requestidlepromise",level:3},{value:"close()",id:"close",level:3},{value:"remove()",id:"remove",level:3},{value:"isRxDatabase",id:"isrxdatabase",level:3},{value:"collections$",id:"collections",level:3}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.header,{children:(0,a.jsx)(s.h1,{id:"rxdatabase",children:"RxDatabase"})}),"\n",(0,a.jsx)(s.p,{children:"A RxDatabase-Object contains your collections and handles the synchronization of change-events."}),"\n",(0,a.jsx)(s.h2,{id:"creation",children:"Creation"}),"\n",(0,a.jsxs)(s.p,{children:["The database is created by the asynchronous ",(0,a.jsx)(s.code,{children:".createRxDatabase()"})," function of the core RxDB module. It has the following parameters:"]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- name"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- RxStorage"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* Optional parameters: */"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myPassword'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- password (optional)"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- multiInstance (optional, default: true)"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" eventReduce"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- eventReduce (optional, default: false)"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" cleanupPolicy"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {} "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// <- custom cleanup policy (optional) "})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h3,{id:"name",children:"name"}),"\n",(0,a.jsxs)(s.p,{children:["The database-name is a string which uniquely identifies the database. When two RxDatabases have the same name and use the same ",(0,a.jsx)(s.code,{children:"RxStorage"}),", their data can be assumed as equal and they will share events between each other.\nDepending on the storage or adapter this can also be used to define the filesystem folder of your data."]}),"\n",(0,a.jsx)(s.h3,{id:"storage",children:"storage"}),"\n",(0,a.jsxs)(s.p,{children:["RxDB works on top of an implementation of the ",(0,a.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," interface. This interface is an abstraction that allows you to use different underlying databases that actually handle the documents. Depending on your use case you might use a different ",(0,a.jsx)(s.code,{children:"storage"})," with different tradeoffs in performance, bundle size or supported runtimes."]}),"\n",(0,a.jsxs)(s.p,{children:["There are many ",(0,a.jsx)(s.code,{children:"RxStorage"})," implementations that can be used depending on the JavaScript environment and performance requirements.\nFor example you can use the ",(0,a.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"})," in the browser or use the ",(0,a.jsx)(s.a,{href:"/rx-storage-mongodb.html",children:"MongoDB RxStorage"})," in Node.js."]}),"\n",(0,a.jsxs)(s.ul,{children:["\n",(0,a.jsx)(s.li,{children:(0,a.jsx)(s.a,{href:"/rx-storage.html",children:"List of RxStorage implementations"})}),"\n"]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// use the LocalStroage that stores data in the browser."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// ...or use the MongoDB RxStorage in Node.js."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageMongoDB } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-mongodb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" dbMongo"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMongoDB"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" connection"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mongodb://localhost:27017,localhost:27018,localhost:27019'"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h3,{id:"password",children:"password"}),"\n",(0,a.jsxs)(s.p,{children:[(0,a.jsx)(s.code,{children:"(optional)"}),"\nIf you want to use encrypted fields in the collections of a database, you have to set a password for it. The password must be a string with at least 12 characters."]}),"\n",(0,a.jsxs)(s.p,{children:[(0,a.jsx)(s.a,{href:"/encryption.html",children:"Read more about encryption here"}),"."]}),"\n",(0,a.jsx)(s.h3,{id:"multiinstance",children:"multiInstance"}),"\n",(0,a.jsxs)(s.p,{children:[(0,a.jsx)(s.code,{children:"(optional=true)"}),"\nWhen you create more than one instance of the same database in a single javascript-runtime, you should set ",(0,a.jsx)(s.code,{children:"multiInstance"})," to ",(0,a.jsx)(s.code,{children:"true"}),". This will enable the event sharing between the two instances. For example when the user has opened multiple browser windows, events will be shared between them so that both windows react to the same changes.\n",(0,a.jsx)(s.code,{children:"multiInstance"})," should be set to ",(0,a.jsx)(s.code,{children:"false"})," when you have single-instances like a single Node.js-process, a react-native-app, a cordova-app or a single-window ",(0,a.jsx)(s.a,{href:"/electron-database.html",children:"electron"})," app which can decrease the startup time because no instance coordination has to be done."]}),"\n",(0,a.jsx)(s.h3,{id:"eventreduce",children:"eventReduce"}),"\n",(0,a.jsx)(s.p,{children:(0,a.jsx)(s.code,{children:"(optional=false)"})}),"\n",(0,a.jsxs)(s.p,{children:["One big benefit of having a realtime database is that big performance optimizations can be done when the database knows a query is observed and the updated results are needed continuously. RxDB uses the ",(0,a.jsx)(s.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce Algorithm"})," to optimize observer or recurring queries."]}),"\n",(0,a.jsxs)(s.p,{children:["For better performance, you should always set ",(0,a.jsx)(s.code,{children:"eventReduce: true"}),". This will also be the default in the next major RxDB version."]}),"\n",(0,a.jsx)(s.h3,{id:"ignoreduplicate",children:"ignoreDuplicate"}),"\n",(0,a.jsxs)(s.p,{children:[(0,a.jsx)(s.code,{children:"(optional=false)"}),"\nIf you create multiple RxDatabase-instances with the same name and same adapter, it's very likely that you have done something wrong.\nTo prevent this common mistake, RxDB will throw an error when you do this.\nIn some rare cases like unit-tests, you want to do this intentional by setting ",(0,a.jsx)(s.code,{children:"ignoreDuplicate"})," to ",(0,a.jsx)(s.code,{children:"true"}),". Because setting ",(0,a.jsx)(s.code,{children:"ignoreDuplicate: true"})," in production will decrease the performance by having multiple instances of the same database, ",(0,a.jsx)(s.code,{children:"ignoreDuplicate"})," is only allowed to be set in ",(0,a.jsx)(s.a,{href:"/dev-mode.html",children:"dev-mode"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db1"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ignoreDuplicate"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db2"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ignoreDuplicate"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // this create-call will not throw because you explicitly allow it"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h3,{id:"closeduplicates",children:"closeDuplicates"}),"\n",(0,a.jsx)(s.p,{children:(0,a.jsx)(s.code,{children:"(optional=false)"})}),"\n",(0,a.jsx)(s.p,{children:"Closes all other RxDatabases instances that have the same storage+name combination."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db1"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" closeDuplicates"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db2"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" closeDuplicates"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // this create-call will close db1"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// db1 is now closed."})})]})})}),"\n",(0,a.jsx)(s.h3,{id:"hashfunction",children:"hashFunction"}),"\n",(0,a.jsxs)(s.p,{children:["By default, RxDB will use ",(0,a.jsx)(s.code,{children:"crypto.subtle.digest('SHA-256', data)"})," for hashing. If you need a different hash function or the ",(0,a.jsx)(s.code,{children:"crypto.subtle"})," API is not supported in your JavaScript runtime, you can provide an own hash function instead. A hash function gets a string as input and returns a ",(0,a.jsx)(s.code,{children:"Promise"})," that resolves a string."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// example hash function that runs in plain JavaScript"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { sha256 } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'ohash'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" myOwnHashFunction"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(input"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".resolve"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"sha256"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(input));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" hashFunction"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" myOwnHashFunction"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsxs)(s.p,{children:["If you get the error message ",(0,a.jsx)(s.code,{children:"TypeError: Cannot read properties of undefined (reading 'digest')"})," this likely means that you are neither running on ",(0,a.jsx)(s.code,{children:"localhost"})," nor on ",(0,a.jsx)(s.code,{children:"https"})," which is why your browser might not allow access to ",(0,a.jsx)(s.code,{children:"crypto.subtle.digest"}),"."]}),"\n",(0,a.jsx)(s.h2,{id:"methods",children:"Methods"}),"\n",(0,a.jsx)(s.h3,{id:"observe-with-",children:"Observe with $"}),"\n",(0,a.jsxs)(s.p,{children:["Calling this will return an ",(0,a.jsx)(s.a,{href:"http://reactivex.io/documentation/observable.html",children:"rxjs-Observable"})," which streams all write events of the ",(0,a.jsx)(s.code,{children:"RxDatabase"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDb"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(changeEvent "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(changeEvent));"})]})})})}),"\n",(0,a.jsx)(s.h3,{id:"exportjson",children:"exportJSON()"}),"\n",(0,a.jsxs)(s.p,{children:["Use this function to create a json-export from every piece of data in every collection of this database. You can pass ",(0,a.jsx)(s.code,{children:"true"})," as a parameter to decrypt the encrypted data-fields of your document."]}),"\n",(0,a.jsxs)(s.p,{children:["Before ",(0,a.jsx)(s.code,{children:"exportJSON()"})," and ",(0,a.jsx)(s.code,{children:"importJSON()"})," can be used, you have to add the ",(0,a.jsx)(s.code,{children:"json-dump"})," plugin."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBJsonDumpPlugin } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/json-dump'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBJsonDumpPlugin);"})]})]})})}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exportJSON"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .then"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(json "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(json));"})]})]})})}),"\n",(0,a.jsx)(s.h3,{id:"importjson",children:"importJSON()"}),"\n",(0,a.jsx)(s.p,{children:"To import the json-dumps into your database, use this function."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// import the dump to the database"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"emptyDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".importJSON"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(json)"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .then"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'done'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]})]})})}),"\n",(0,a.jsx)(s.h3,{id:"backup",children:"backup()"}),"\n",(0,a.jsxs)(s.p,{children:["Writes the current (or ongoing) database state to the filesystem. ",(0,a.jsx)(s.a,{href:"/backup.html",children:"Read more"})]}),"\n",(0,a.jsx)(s.h3,{id:"waitforleadership",children:"waitForLeadership()"}),"\n",(0,a.jsxs)(s.p,{children:["Returns a Promise which resolves when the RxDatabase becomes ",(0,a.jsx)(s.a,{href:"/leader-election.html",children:"elected leader"}),"."]}),"\n",(0,a.jsx)(s.h3,{id:"requestidlepromise",children:"requestIdlePromise()"}),"\n",(0,a.jsxs)(s.p,{children:["Returns a promise which resolves when the database is in idle. This works similar to ",(0,a.jsx)(s.a,{href:"https://developer.mozilla.org/de/docs/Web/API/Window/requestIdleCallback",children:"requestIdleCallback"})," but tracks the idle-ness of the database instead of the CPU.\nUse this for semi-important tasks like cleanups which should not affect the speed of important tasks."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".requestIdlePromise"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // this will run at the moment the database has nothing else to do"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".customCleanupFunction"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// with timeout"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".requestIdlePromise"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"1000"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* time in ms */"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // this will run at the moment the database has nothing else to do"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // or the timeout has passed"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".customCleanupFunction"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "})]})})}),"\n",(0,a.jsx)(s.h3,{id:"close",children:"close()"}),"\n",(0,a.jsxs)(s.p,{children:["Closes the databases object-instance. This is to free up memory and stop all observers and replications.\nReturns a ",(0,a.jsx)(s.code,{children:"Promise"})," that resolves when the database is closed.\nClosing a database will not remove the databases data. When you create the database again with ",(0,a.jsx)(s.code,{children:"createRxDatabase()"}),", all data will still be there."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".close"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),"\n",(0,a.jsx)(s.h3,{id:"remove",children:"remove()"}),"\n",(0,a.jsx)(s.p,{children:"Wipes all documents from the storage. Use this to free up disc space."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// database instance is now gone"})})]})})}),"\n",(0,a.jsxs)(s.p,{children:["You can also clear a database without removing its instance by using ",(0,a.jsx)(s.code,{children:"removeRxDatabase()"}),". This is useful if you want to migrate data or reset the users state by renaming the database. Then you can remove the previous data with ",(0,a.jsx)(s.code,{children:"removeRxDatabase()"})," without creating a RxDatabase first. Notice that this will only remove the\nstored data on the storage. It will not clear the cache of any ",(0,a.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"})," instances."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { removeRxDatabase } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"removeRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'mydatabasename'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'localstorage'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,a.jsx)(s.h3,{id:"isrxdatabase",children:"isRxDatabase"}),"\n",(0,a.jsx)(s.p,{children:"Returns true if the given object is an instance of RxDatabase. Returns false if not."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { isRxDatabase } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" is"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" isRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(myObj);"})]})]})})}),"\n",(0,a.jsx)(s.h3,{id:"collections",children:"collections$"}),"\n",(0,a.jsxs)(s.p,{children:["Emits events whenever a ",(0,a.jsx)(s.a,{href:"/rx-collection.html",children:"RxCollection"})," is added or removed to the instance of the RxDatabase. Notice that this only emits the JavaScript instance of the RxCollection class, it does not emit events across browser tabs."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sub"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"collections$"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(event "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(event);"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// -> emits the event"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"sub"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".unsubscribe"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,a.jsx)(s,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>i,x:()=>t});var r=n(6540);const a={},o=r.createContext(a);function i(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/25626d15.27c1b41f.js b/docs/assets/js/25626d15.27c1b41f.js
deleted file mode 100644
index 6c3bcc50301..00000000000
--- a/docs/assets/js/25626d15.27c1b41f.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[268],{7258(e,t,i){i.r(t),i.d(t,{default:()=>n});var s=i(4586),r=i(8711),c=i(6540),l=i(8141),h=i(4848);function n(){const{siteConfig:e}=(0,s.A)();return(0,c.useEffect)(()=>{(0,l.c)("join_chat",.4)}),(0,h.jsx)(r.A,{title:`Chat - ${e.title}`,description:"RxDB Community Chat",children:(0,h.jsx)("main",{children:(0,h.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,h.jsx)("a",{href:"/",children:(0,h.jsx)("div",{className:"logo",children:(0,h.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,h.jsx)("h1",{children:"\ud83d\udcac RxDB Chat"}),(0,h.jsx)("p",{children:(0,h.jsx)("b",{children:"You will be redirected in a few seconds."})}),(0,h.jsx)("p",{children:(0,h.jsx)("a",{href:"https://discord.gg/krNZtjZtpu",children:"Click here to open Chat"})}),(0,h.jsx)("meta",{httpEquiv:"Refresh",content:"1; url=https://discord.gg/krNZtjZtpu"})]})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/2564bf4f.b4905e08.js b/docs/assets/js/2564bf4f.b4905e08.js
deleted file mode 100644
index e89eb838547..00000000000
--- a/docs/assets/js/2564bf4f.b4905e08.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6724],{8365(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"rxdb-tradeoffs","title":"RxDB Tradeoffs - Why NoSQL Triumphs on the Client","description":"Uncover RxDB\'s approach to modern database needs. From JSON-based queries to conflict handling without transactions, learn RxDB\'s unique tradeoffs.","source":"@site/docs/rxdb-tradeoffs.md","sourceDirName":".","slug":"/rxdb-tradeoffs.html","permalink":"/rxdb-tradeoffs.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB Tradeoffs - Why NoSQL Triumphs on the Client","slug":"rxdb-tradeoffs.html","description":"Uncover RxDB\'s approach to modern database needs. From JSON-based queries to conflict handling without transactions, learn RxDB\'s unique tradeoffs."}}');var a=n(4848),i=n(8453);const o={title:"RxDB Tradeoffs - Why NoSQL Triumphs on the Client",slug:"rxdb-tradeoffs.html",description:"Uncover RxDB's approach to modern database needs. From JSON-based queries to conflict handling without transactions, learn RxDB's unique tradeoffs."},t="RxDB Tradeoffs",l={},d=[{value:"Why not SQL syntax",id:"why-not-sql-syntax",level:2},{value:"SQL is made for database servers",id:"sql-is-made-for-database-servers",level:3},{value:"Typescript support",id:"typescript-support",level:3},{value:"Composeable queries",id:"composeable-queries",level:3},{value:"Why Document based (NoSQL)",id:"why-document-based-nosql",level:2},{value:"Javascript is made to work with objects",id:"javascript-is-made-to-work-with-objects",level:3},{value:"Caching",id:"caching",level:3},{value:"EventReduce",id:"eventreduce",level:3},{value:"Easier to use with typescript",id:"easier-to-use-with-typescript",level:3},{value:"Why no transactions",id:"why-no-transactions",level:2},{value:"Why no relations",id:"why-no-relations",level:2},{value:"Why is a schema required",id:"why-is-a-schema-required",level:2}];function c(e){const s={a:"a",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.header,{children:(0,a.jsx)(s.h1,{id:"rxdb-tradeoffs",children:"RxDB Tradeoffs"})}),"\n",(0,a.jsxs)(s.p,{children:[(0,a.jsx)(s.a,{href:"https://rxdb.info",children:"RxDB"})," is client-side, ",(0,a.jsx)(s.a,{href:"/offline-first.html",children:"offline first"})," Database for JavaScript applications.\nWhile RxDB could be used on the server side, most people use it on the client side together with an UI based application.\nTherefore RxDB was optimized for client side applications and had to take completely different tradeoffs than what a server side database would do."]}),"\n",(0,a.jsx)(s.h2,{id:"why-not-sql-syntax",children:"Why not SQL syntax"}),"\n",(0,a.jsxs)(s.p,{children:["When you ask people which database they would want for browsers, the most answer I hear is ",(0,a.jsx)(s.em,{children:"something SQL based like SQLite"}),".\nThis makes sense, SQL is a query language that most developers had learned in school/university and it is reusable across various database solutions.\nBut for RxDB (and other client side databases), using SQL is not a good option and instead it operates on document writes and the JSON based ",(0,a.jsx)(s.strong,{children:"Mango-query"})," syntax for querying."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// A Mango Query"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ age"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,a.jsx)(s.h3,{id:"sql-is-made-for-database-servers",children:"SQL is made for database servers"}),"\n",(0,a.jsxs)(s.p,{children:["SQL is made to be used to run operations against a database server. You send a SQL string like ",(0,a.jsx)(s.code,{children:"SELECT SUM(column_name)..."})," to the database server and the server then runs all operations required to calculate the result and only send back that result.\nThis saves performance on the application side and ensures that the application itself is not blocked."]}),"\n",(0,a.jsxs)(s.p,{children:["But RxDB is a client-side database that runs ",(0,a.jsx)(s.strong,{children:"inside"})," of the application. There is no performance difference if the ",(0,a.jsx)(s.code,{children:"SUM()"})," query is run inside of the database or at the application level where a ",(0,a.jsx)(s.code,{children:"Array.reduce()"})," call calculates the result."]}),"\n",(0,a.jsx)(s.h3,{id:"typescript-support",children:"Typescript support"}),"\n",(0,a.jsxs)(s.p,{children:["SQL is ",(0,a.jsx)(s.code,{children:"string"})," based and therefore you need additional IDE tooling to ensure that your written database code is valid.\nUsing the Mango Query syntax instead, TypeScript can be used validate the queries and to autocomplete code and knows which fields do exist and which do not. By doing so, the correctness of queries can be ensured at compile-time instead of run-time."]}),"\n",(0,a.jsx)("p",{align:"center",children:(0,a.jsx)("img",{src:"./files/typescript-query-validation.png",alt:"TypeScript Query Validation"})}),"\n",(0,a.jsx)(s.h3,{id:"composeable-queries",children:"Composeable queries"}),"\n",(0,a.jsxs)(s.p,{children:["By using JSON based Mango Queries, it is easy to compose queries in plain JavaScript.\nFor example if you have any given query and want to add the condition ",(0,a.jsx)(s.code,{children:"user MUST BE 'foobar'"}),", you can just add the condition to the selector without having to parse and understand a complex SQL string."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"query"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"selector"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".user "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})})})}),"\n",(0,a.jsx)(s.p,{children:"Even merging the selectors of multiple queries is not a problem:"}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"queryA"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".selector "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $and"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" queryA"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".selector"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" queryB"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".selector"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"why-document-based-nosql",children:"Why Document based (NoSQL)"}),"\n",(0,a.jsx)(s.p,{children:"Like other NoSQL databases, RxDB operates data on document level. It has no concept of tables, rows and columns. Instead we have collections, documents and fields."}),"\n",(0,a.jsx)(s.h3,{id:"javascript-is-made-to-work-with-objects",children:"Javascript is made to work with objects"}),"\n",(0,a.jsx)(s.h3,{id:"caching",children:"Caching"}),"\n",(0,a.jsx)(s.h3,{id:"eventreduce",children:"EventReduce"}),"\n",(0,a.jsx)(s.h3,{id:"easier-to-use-with-typescript",children:"Easier to use with typescript"}),"\n",(0,a.jsx)(s.p,{children:"Because of the document based approach, TypeScript can know the exact type of the query response while a SQL query could return anything from a number over a set of rows or a complex construct."}),"\n",(0,a.jsx)(s.h2,{id:"why-no-transactions",children:"Why no transactions"}),"\n",(0,a.jsxs)(s.ul,{children:["\n",(0,a.jsx)(s.li,{children:"Does not work with offline-first"}),"\n",(0,a.jsx)(s.li,{children:"Does not work with multi-tab"}),"\n",(0,a.jsx)(s.li,{children:"Easier conflict handling on document level"}),"\n"]}),"\n",(0,a.jsx)(s.p,{children:"-- Instead of transactions, rxdb works with revisions"}),"\n",(0,a.jsx)(s.h2,{id:"why-no-relations",children:"Why no relations"}),"\n",(0,a.jsxs)(s.ul,{children:["\n",(0,a.jsx)(s.li,{children:"Does not work with easy replication"}),"\n"]}),"\n",(0,a.jsx)(s.h2,{id:"why-is-a-schema-required",children:"Why is a schema required"}),"\n",(0,a.jsxs)(s.ul,{children:["\n",(0,a.jsx)(s.li,{children:"migration of data on clients is hard"}),"\n",(0,a.jsx)(s.li,{children:"Why jsonschema"}),"\n"]}),"\n",(0,a.jsx)(s.h2,{id:""})]})}function h(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,a.jsx)(s,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},8453(e,s,n){n.d(s,{R:()=>o,x:()=>t});var r=n(6540);const a={},i=r.createContext(a);function o(e){const s=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),r.createElement(i.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/25a43fd4.f20d897d.js b/docs/assets/js/25a43fd4.f20d897d.js
deleted file mode 100644
index b51a28e67bd..00000000000
--- a/docs/assets/js/25a43fd4.f20d897d.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4812],{8453(e,r,s){s.d(r,{R:()=>a,x:()=>l});var n=s(6540);const o={},i=n.createContext(o);function a(e){const r=n.useContext(i);return n.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function l(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),n.createElement(i.Provider,{value:r},e.children)}},8803(e,r,s){s.r(r),s.d(r,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>n,toc:()=>d});const n=JSON.parse('{"id":"rx-storage-shared-worker","title":"Boost Performance with SharedWorker RxStorage","description":"Tap into single-instance storage with RxDB\'s SharedWorker. Improve efficiency, cut duplication, and keep your app lightning-fast across tabs.","source":"@site/docs/rx-storage-shared-worker.md","sourceDirName":".","slug":"/rx-storage-shared-worker.html","permalink":"/rx-storage-shared-worker.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Boost Performance with SharedWorker RxStorage","slug":"rx-storage-shared-worker.html","description":"Tap into single-instance storage with RxDB\'s SharedWorker. Improve efficiency, cut duplication, and keep your app lightning-fast across tabs."},"sidebar":"tutorialSidebar","previous":{"title":"Worker RxStorage \ud83d\udc51","permalink":"/rx-storage-worker.html"},"next":{"title":"Memory Mapped RxStorage \ud83d\udc51","permalink":"/rx-storage-memory-mapped.html"}}');var o=s(4848),i=s(8453);const a={title:"Boost Performance with SharedWorker RxStorage",slug:"rx-storage-shared-worker.html",description:"Tap into single-instance storage with RxDB's SharedWorker. Improve efficiency, cut duplication, and keep your app lightning-fast across tabs."},l="SharedWorker RxStorage",t={},d=[{value:"Usage",id:"usage",level:2},{value:"On the SharedWorker process",id:"on-the-sharedworker-process",level:3},{value:"On the main process",id:"on-the-main-process",level:3},{value:"Pre-build workers",id:"pre-build-workers",level:2},{value:"Building a custom worker",id:"building-a-custom-worker",level:2},{value:"Passing in a SharedWorker instance",id:"passing-in-a-sharedworker-instance",level:2},{value:"Set multiInstance: false",id:"set-multiinstance-false",level:2},{value:"Replication with SharedWorker",id:"replication-with-sharedworker",level:2},{value:"Limitations",id:"limitations",level:3},{value:"FAQ",id:"faq",level:3}];function c(e){const r={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...e.components},{Details:s}=r;return s||function(e,r){throw new Error("Expected "+(r?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(r.header,{children:(0,o.jsx)(r.h1,{id:"sharedworker-rxstorage",children:"SharedWorker RxStorage"})}),"\n",(0,o.jsxs)(r.p,{children:["The SharedWorker ",(0,o.jsx)(r.a,{href:"/rx-storage.html",children:"RxStorage"})," uses the ",(0,o.jsx)(r.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker",children:"SharedWorker API"})," to run the storage inside of a separate JavaScript process ",(0,o.jsx)(r.strong,{children:"in browsers"}),". Compared to a normal ",(0,o.jsx)(r.a,{href:"/rx-storage-worker.html",children:"WebWorker"}),", the SharedWorker is created exactly once, even when there are multiple browser tabs opened. Because of having exactly one worker, multiple performance optimizations can be done because the storage itself does not have to handle multiple opened database connections."]}),"\n",(0,o.jsx)(r.admonition,{title:"Premium",type:"note",children:(0,o.jsxs)(r.p,{children:["This plugin is part of ",(0,o.jsx)(r.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"}),". It is not part of the default RxDB module."]})}),"\n",(0,o.jsx)(r.h2,{id:"usage",children:"Usage"}),"\n",(0,o.jsx)(r.h3,{id:"on-the-sharedworker-process",children:"On the SharedWorker process"}),"\n",(0,o.jsxs)(r.p,{children:["In the worker process JavaScript file, you have wrap the original RxStorage with ",(0,o.jsx)(r.code,{children:"getRxStorageIndexedDB()"}),"."]}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// shared-worker.ts"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { exposeWorkerRxStorage } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { "})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/indexeddb'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:"exposeWorkerRxStorage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can wrap any implementation of the RxStorage interface"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * into a worker."})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here we use the IndexedDB RxStorage."})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(r.h3,{id:"on-the-main-process",children:"On the main process"}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageSharedWorker } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-indexeddb'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSharedWorker"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * Contains any value that can be used as parameter"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * to the SharedWorker constructor of thread.js"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * Most likely you want to put the path to the shared-worker.js file in here."})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker?retiredLocale=de"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'path/to/shared-worker.js'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * (Optional) options"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * for the worker."})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" workerOptions"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'module'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" credentials"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'omit'"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(r.h2,{id:"pre-build-workers",children:"Pre-build workers"}),"\n",(0,o.jsxs)(r.p,{children:["The ",(0,o.jsx)(r.code,{children:"shared-worker.js"})," must be a self containing JavaScript file that contains all dependencies in a bundle.\nTo make it easier for you, RxDB ships with pre-bundles worker files that are ready to use.\nYou can find them in the folder ",(0,o.jsx)(r.code,{children:"node_modules/rxdb-premium/dist/workers"})," after you have installed the ",(0,o.jsx)(r.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51 Plugin"}),". From there you can copy them to a location where it can be served from the webserver and then use their path to create the ",(0,o.jsx)(r.code,{children:"RxDatabase"})]}),"\n",(0,o.jsxs)(r.p,{children:["Any valid ",(0,o.jsx)(r.code,{children:"worker.js"})," JavaScript file can be used both, for normal Workers and SharedWorkers."]}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageSharedWorker } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSharedWorker"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * Path to where the copied file from node_modules/rxdb-premium/dist/workers"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * is reachable from the webserver."})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/indexeddb.shared-worker.js'"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(r.h2,{id:"building-a-custom-worker",children:"Building a custom worker"}),"\n",(0,o.jsxs)(r.p,{children:["To build a custom ",(0,o.jsx)(r.code,{children:"worker.js"})," file, check out the webpack config at the ",(0,o.jsx)(r.a,{href:"/rx-storage-worker.html#building-a-custom-worker",children:"worker"})," documentation. Any worker file form the worker storage can also be used in a shared worker because ",(0,o.jsx)(r.code,{children:"exposeWorkerRxStorage"})," detects where it runs and exposes the correct messaging endpoints."]}),"\n",(0,o.jsx)(r.h2,{id:"passing-in-a-sharedworker-instance",children:"Passing in a SharedWorker instance"}),"\n",(0,o.jsxs)(r.p,{children:["Instead of setting an url as ",(0,o.jsx)(r.code,{children:"workerInput"}),", you can also specify a function that returns a new ",(0,o.jsx)(r.code,{children:"SharedWorker"})," instance when called. This is mostly used when you have a custom worker file and dynamically import it.\nThis works equal to the ",(0,o.jsx)(r.a,{href:"/rx-storage-worker.html#passing-in-a-worker-instance",children:"workerInput of the Worker Storage"})]}),"\n",(0,o.jsx)(r.h2,{id:"set-multiinstance-false",children:"Set multiInstance: false"}),"\n",(0,o.jsxs)(r.p,{children:["When you know that you only ever create your RxDatabase inside of the shared worker, you might want to set ",(0,o.jsx)(r.code,{children:"multiInstance: false"})," to prevent sending change events across JavaScript realms and to improve performance. Do not set this when you also create the same storage on another realm, like when you have the same RxDatabase once inside the shared worker and once on the main thread."]}),"\n",(0,o.jsx)(r.h2,{id:"replication-with-sharedworker",children:"Replication with SharedWorker"}),"\n",(0,o.jsxs)(r.p,{children:["When a SharedWorker RxStorage is used, it is recommended to run the replication ",(0,o.jsx)(r.strong,{children:"inside"})," of the worker. This is the best option for performance. You can do that by opening another ",(0,o.jsx)(r.a,{href:"/rx-database.html",children:"RxDatabase"})," inside of it and starting the replication there. If you are not concerned about performance, you can still start replication on the main thread instead. But you should never run replication on both the main thread ",(0,o.jsx)(r.strong,{children:"and"})," the worker."]}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// shared-worker.ts"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { exposeWorkerRxStorage } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { "})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" addRxPlugin"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" RxDBReplicationGraphQLPlugin"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-graphql'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBReplicationGraphQLPlugin);"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" baseStorage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// first expose the RxStorage to the outside"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:"exposeWorkerRxStorage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" baseStorage"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * Then create a normal RxDatabase and RxCollections"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * and start the replication."})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" baseStorage"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"}"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:"humans"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:".syncGraphQL"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]})]})})}),"\n",(0,o.jsx)(r.h3,{id:"limitations",children:"Limitations"}),"\n",(0,o.jsxs)(r.ul,{children:["\n",(0,o.jsxs)(r.li,{children:["The SharedWorker API is ",(0,o.jsx)(r.a,{href:"https://caniuse.com/sharedworkers",children:"not available in some mobile browser"})]}),"\n"]}),"\n",(0,o.jsx)(r.h3,{id:"faq",children:"FAQ"}),"\n",(0,o.jsxs)(s,{children:[(0,o.jsx)("summary",{children:"Can I use this plugin with a Service Worker?"}),(0,o.jsx)("div",{children:(0,o.jsxs)(r.p,{children:["No. A Service Worker ",(0,o.jsx)("a",{href:"https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API",children:"is not the same"})," as a Shared Worker. While you can use RxDB inside of a ServiceWorker, you cannot use the ServiceWorker as a RxStorage that gets accessed by an outside RxDatabase instance."]})})]})]})}function h(e={}){const{wrapper:r}={...(0,i.R)(),...e.components};return r?(0,o.jsx)(r,{...e,children:(0,o.jsx)(c,{...e})}):c(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/26b8a621.0fdedfc5.js b/docs/assets/js/26b8a621.0fdedfc5.js
deleted file mode 100644
index 7a65f0908bf..00000000000
--- a/docs/assets/js/26b8a621.0fdedfc5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2055],{1673(e,t,n){n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>c,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"replication-p2p","title":"Seamless P2P Data Sync","description":"Discover how replication-webrtc ensures secure P2P data sync and real-time collaboration with RxDB. Explore the future of offline-first apps!","source":"@site/docs/replication-p2p.md","sourceDirName":".","slug":"/replication-p2p.html","permalink":"/replication-p2p.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Seamless P2P Data Sync","slug":"replication-p2p.html","description":"Discover how replication-webrtc ensures secure P2P data sync and real-time collaboration with RxDB. Explore the future of offline-first apps!"}}');var o=n(4848),i=n(8453);const a={title:"Seamless P2P Data Sync",slug:"replication-p2p.html",description:"Discover how replication-webrtc ensures secure P2P data sync and real-time collaboration with RxDB. Explore the future of offline-first apps!"},c="The RxDB Plugin replication-p2p has been renamed to replication-webrtc",s={},l=[];function p(e){const t={a:"a",code:"code",h1:"h1",header:"header",p:"p",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.header,{children:(0,o.jsxs)(t.h1,{id:"the-rxdb-plugin-replication-p2p-has-been-renamed-to-replication-webrtc",children:["The RxDB Plugin ",(0,o.jsx)(t.code,{children:"replication-p2p"})," has been renamed to ",(0,o.jsx)(t.code,{children:"replication-webrtc"})]})}),"\n",(0,o.jsxs)(t.p,{children:["The new documentation page has been moved to ",(0,o.jsx)(t.a,{href:"/replication-webrtc.html",children:"here"})]}),"\n",(0,o.jsx)("meta",{"http-equiv":"refresh",content:"0; url=https://rxdb.info/replication-webrtc.html"})]})}function h(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(p,{...e})}):p(e)}},8453(e,t,n){n.d(t,{R:()=>a,x:()=>c});var r=n(6540);const o={},i=r.createContext(o);function a(e){const t=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function c(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),r.createElement(i.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/280a2389.3dec6d46.js b/docs/assets/js/280a2389.3dec6d46.js
deleted file mode 100644
index 3579697ed66..00000000000
--- a/docs/assets/js/280a2389.3dec6d46.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[176],{522(e,i,l){l.r(i),l.d(i,{default:()=>h});var r=l(4586),s=l(8711),c=l(6540),t=l(8141),d=l(4848);function h(){const{siteConfig:e}=(0,r.A)();return(0,c.useEffect)(()=>{(0,t.c)("meeting-link-clicked",40,1)}),(0,d.jsx)(s.A,{title:`Meeting - ${e.title}`,description:"RxDB Meeting Scheduler",children:(0,d.jsx)("main",{children:(0,d.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,d.jsx)("a",{href:"/",children:(0,d.jsx)("div",{className:"logo",children:(0,d.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,d.jsx)("h1",{children:"RxDB Meeting Scheduler"}),(0,d.jsx)("p",{children:(0,d.jsx)("b",{children:"You will be redirected in a few seconds."})}),(0,d.jsx)("p",{children:(0,d.jsx)("a",{href:"https://rxdb.pipedrive.com/scheduler/QBbGlDC4/schedulr",children:"Click here"})}),(0,d.jsx)("meta",{httpEquiv:"Refresh",content:"0; url=https://rxdb.pipedrive.com/scheduler/QBbGlDC4/schedulr"})]})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/294ac9d5.a530f746.js b/docs/assets/js/294ac9d5.a530f746.js
deleted file mode 100644
index 980c2961119..00000000000
--- a/docs/assets/js/294ac9d5.a530f746.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8744],{2606(n,s,e){e.r(s),e.d(s,{assets:()=>t,contentTitle:()=>a,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"plugins","title":"Creating Plugins","description":"Creating your own plugin is very simple. A plugin is basically a javascript-object which overwrites or extends RxDB\'s internal classes, prototypes, and hooks.","source":"@site/docs/plugins.md","sourceDirName":".","slug":"/plugins.html","permalink":"/plugins.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Creating Plugins","slug":"plugins.html"},"sidebar":"tutorialSidebar","previous":{"title":"Query Cache","permalink":"/query-cache.html"},"next":{"title":"Errors","permalink":"/errors.html"}}');var o=e(4848),i=e(8453);const l={title:"Creating Plugins",slug:"plugins.html"},a="Creating Plugins",t={},c=[{value:"rxdb",id:"rxdb",level:2},{value:"prototypes",id:"prototypes",level:2},{value:"overwritable",id:"overwritable",level:2}];function d(n){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",...(0,i.R)(),...n.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"creating-plugins",children:"Creating Plugins"})}),"\n",(0,o.jsx)(s.p,{children:"Creating your own plugin is very simple. A plugin is basically a javascript-object which overwrites or extends RxDB's internal classes, prototypes, and hooks."}),"\n",(0,o.jsx)(s.p,{children:"A basic plugin:"}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPlugin"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" rxdb"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // this must be true so rxdb knows that this is a rxdb-plugin"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional) init() method"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * that is called when the plugin is added to RxDB for the first time."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" init"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // import other plugins or initialize stuff"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * every value in this object can manipulate the prototype of the keynames class"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can manipulate every prototype in this list:"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" https://github.com/pubkey/rxdb/blob/master/src/plugin.ts#L22"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" prototypes"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * add a function to RxCollection so you can call 'myCollection.hello()'"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" *"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@param"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" {object}"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" prototype of RxCollection"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" RxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (proto) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" proto"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"hello"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'world'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * some methods are static and can be overwritten in the overwritable-object"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" overwritable"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" validatePassword"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(password) {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (password "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&&"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" typeof"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" password "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!=="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ||"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" password"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" <"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" throw"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" TypeError"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'password is not valid'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * you can add hooks to the hook-list"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" hooks"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * add a `foo` property to each document. You can then call myDocument.foo (='bar')"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDocument"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can either add the hook running 'before' or 'after'"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * the hooks of other plugins."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" after"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc) {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".foo "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// now you can import the plugin into rxdb"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(myPlugin);"})]})]})})}),"\n",(0,o.jsx)(s.h1,{id:"properties",children:"Properties"}),"\n",(0,o.jsx)(s.h2,{id:"rxdb",children:"rxdb"}),"\n",(0,o.jsxs)(s.p,{children:["The ",(0,o.jsx)(s.code,{children:"rxdb"}),"-property signals that this plugin is an rxdb-plugin. The value should always be ",(0,o.jsx)(s.code,{children:"true"}),"."]}),"\n",(0,o.jsx)(s.h2,{id:"prototypes",children:"prototypes"}),"\n",(0,o.jsxs)(s.p,{children:["The ",(0,o.jsx)(s.code,{children:"prototypes"}),"-property contains a function for each of RxDB's internal prototype that you want to manipulate. Each function gets the prototype-object of the corresponding class as parameter and then can modify it. You can see a list of all available prototypes ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/master/src/plugin.ts",children:"here"})]}),"\n",(0,o.jsx)(s.h2,{id:"overwritable",children:"overwritable"}),"\n",(0,o.jsxs)(s.p,{children:["Some of RxDB's functions are not inside of a class-prototype but are static. You can set and overwrite them with the ",(0,o.jsx)(s.code,{children:"overwritable"}),"-object. You can see a list of all overwritables ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/master/src/overwritable.ts",children:"here"}),"."]}),"\n",(0,o.jsx)(s.h1,{id:"hooks",children:"hooks"}),"\n",(0,o.jsxs)(s.p,{children:["Sometimes you don't want to overwrite an existing RxDB-method, but extend it. You can do this by adding hooks which will be called each time the code jumps into the hooks corresponding call. You can find a list of all hooks ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/master/src/hooks.ts",children:"here"}),"."]}),"\n",(0,o.jsx)(s.h1,{id:"options",children:"options"}),"\n",(0,o.jsx)(s.p,{children:"RxDatabase and RxCollection have an additional options-parameter, which can be filled with any data required be the plugin."}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foo"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" options"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// anything can be passed into the options"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" foo"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ()"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'bar'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Afterwards you can use these options in your plugin."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"collection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"options"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".foo"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'bar'"})]})]})})})]})}function h(n={}){const{wrapper:s}={...(0,i.R)(),...n.components};return s?(0,o.jsx)(s,{...n,children:(0,o.jsx)(d,{...n})}):d(n)}},8453(n,s,e){e.d(s,{R:()=>l,x:()=>a});var r=e(6540);const o={},i=r.createContext(o);function l(n){const s=r.useContext(i);return r.useMemo(function(){return"function"==typeof n?n(s):{...s,...n}},[s,n])}function a(n){let s;return s=n.disableParentContext?"function"==typeof n.components?n.components(o):n.components||o:l(n.components),r.createElement(i.Provider,{value:s},n.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/2aec6989.93b045aa.js b/docs/assets/js/2aec6989.93b045aa.js
deleted file mode 100644
index 2b2df407deb..00000000000
--- a/docs/assets/js/2aec6989.93b045aa.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7498],{4633(e,s,r){r.r(s),r.d(s,{assets:()=>d,contentTitle:()=>l,default:()=>p,frontMatter:()=>t,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"articles/indexeddb-max-storage-limit","title":"IndexedDB Max Storage Size Limit - Detailed Best Practices","description":"Learn how browsers enforce IndexedDB storage size limits, how to test and handle quota exceeded errors, and best practices for storing large amounts of data offline.","source":"@site/docs/articles/indexeddb-max-storage-limit.md","sourceDirName":"articles","slug":"/articles/indexeddb-max-storage-limit.html","permalink":"/articles/indexeddb-max-storage-limit.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"IndexedDB Max Storage Size Limit - Detailed Best Practices","slug":"indexeddb-max-storage-limit.html","description":"Learn how browsers enforce IndexedDB storage size limits, how to test and handle quota exceeded errors, and best practices for storing large amounts of data offline."},"sidebar":"tutorialSidebar","previous":{"title":"Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression","permalink":"/articles/zero-latency-local-first.html"},"next":{"title":"JSON-Based Databases - Why NoSQL and RxDB Simplify App Development","permalink":"/articles/json-based-database.html"}}');var i=r(4848),o=r(8453),a=r(2271);const t={title:"IndexedDB Max Storage Size Limit - Detailed Best Practices",slug:"indexeddb-max-storage-limit.html",description:"Learn how browsers enforce IndexedDB storage size limits, how to test and handle quota exceeded errors, and best practices for storing large amounts of data offline."},l="IndexedDB Max Storage Size Limit",d={},c=[{value:"Why IndexedDB Has a Storage Limit",id:"why-indexeddb-has-a-storage-limit",level:2},{value:"Browser-Specific IndexedDB Limits",id:"browser-specific-indexeddb-limits",level:2},{value:"Checking Your Current IndexedDB Usage",id:"checking-your-current-indexeddb-usage",level:2},{value:"Testing Your App\u2019s IndexedDB Quotas",id:"testing-your-apps-indexeddb-quotas",level:2},{value:"Handling Errors When Limits Are Reached",id:"handling-errors-when-limits-are-reached",level:2},{value:"Tricks to Exceed the Storage Size Limitation",id:"tricks-to-exceed-the-storage-size-limitation",level:2},{value:"IndexedDB Max Size of a Single Object",id:"indexeddb-max-size-of-a-single-object",level:2},{value:"Is There a Time Limit for Data Stored in IndexedDB?",id:"is-there-a-time-limit-for-data-stored-in-indexeddb",level:2},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",hr:"hr",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"indexeddb-max-storage-size-limit",children:"IndexedDB Max Storage Size Limit"})}),"\n",(0,i.jsx)(s.p,{children:"IndexedDB is widely known as the primary browser-based storage API for large client-side data, particularly valuable for modern offline-first applications. These apps aim to keep everything functional and interactive even without an internet connection, which naturally demands substantial local storage. However, IndexedDB has various size limits depending on the browser, disk space, and user settings. Being aware of these constraints is crucial so you can avoid quota errors and deliver a seamless user experience without unexpected data loss."}),"\n",(0,i.jsx)(s.p,{children:"Offline-first apps have grown in popularity because they provide immediate feedback, zero-latency interactions, and resilience in poor network conditions. Storing big data sets, or even entire data models, in IndexedDB has become far more common than in the era of small localStorage or cookie usage. But all this local data is subject to quotas, and that\u2019s exactly what this guide will help you understand and manage."}),"\n",(0,i.jsx)(s.h2,{id:"why-indexeddb-has-a-storage-limit",children:"Why IndexedDB Has a Storage Limit"}),"\n",(0,i.jsxs)(s.p,{children:["Browsers need a way to curb runaway disk usage and safeguard user resources. This is accomplished through ",(0,i.jsx)(s.strong,{children:"quota management"})," policies, which can vary among Chrome, Firefox, Safari, Edge, and others. Some browsers use a percentage of your total disk space, while others rely on a fixed maximum or dynamic approach per origin. These policies are designed to prevent malicious or poorly optimized web pages from consuming an unreasonable amount of user storage."]}),"\n",(0,i.jsx)(s.p,{children:"Chrome (and Chromium-based browsers) typically allow you to use a percentage of the user\u2019s free disk space, whereas Firefox historically prompts users to allow more than 5 MB in mobile or 50 MB in desktop. Safari often sets tighter maximum caps, especially on iOS devices. Edge aligns closely with Chrome\u2019s rules but can also include enterprise or corporate policy overrides. Understanding these default or dynamic limits prepares you to plan your app\u2019s storage needs appropriately."}),"\n",(0,i.jsx)(s.h2,{id:"browser-specific-indexeddb-limits",children:"Browser-Specific IndexedDB Limits"}),"\n",(0,i.jsx)(s.p,{children:"IndexedDB size quotas differ significantly across browsers and platforms. While there isn\u2019t a universal rule, the following table summarizes approximate limits and any notes or caveats you should be aware of:"}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:"Browser"}),(0,i.jsx)(s.th,{children:"Approx. Limit"}),(0,i.jsx)(s.th,{children:"Notes"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Chrome/Chromium"}),(0,i.jsx)(s.td,{children:"Up to ~80% of free disk, per origin cap"}),(0,i.jsx)(s.td,{children:"Often cited as 60 GB on a 100 GB drive. Shared pool approach. Quota usage can prompt partial or extended user approvals."})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Firefox"}),(0,i.jsx)(s.td,{children:"~2 GB (desktop) or ~5 MB initial for mobile"}),(0,i.jsx)(s.td,{children:"Older versions asked permission at 50 MB for desktop. Ephemeral/incognito sessions may require repeated user prompts."})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Safari (iOS)"}),(0,i.jsx)(s.td,{children:"~1 GB per origin (variable)"}),(0,i.jsx)(s.td,{children:"Historically stricter. iOS devices limit quotas further. Behavior can differ between iOS Safari versions or iPadOS."})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Edge"}),(0,i.jsx)(s.td,{children:"Similar to Chrome\u2019s 80% of free space"}),(0,i.jsx)(s.td,{children:"Can be influenced by Windows enterprise policies. Generally aligned with Chromium approach."})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"iOS Safari"}),(0,i.jsx)(s.td,{children:"Typically 1 GB, can be less on older iOS"}),(0,i.jsx)(s.td,{children:"Early iOS versions were known for more aggressive quotas and data eviction on low space."})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Android Chrome"}),(0,i.jsx)(s.td,{children:"Similar to desktop Chrome"}),(0,i.jsx)(s.td,{children:"May exhibit warnings in especially low-storage devices. The same 80% free space logic generally applies."})]})]})]}),"\n",(0,i.jsxs)(s.p,{children:["Historically, these limits have evolved. For instance, older Firefox versions included ",(0,i.jsx)(s.code,{children:"dom.indexedDB.warningQuota"}),", showing a 50 MB prompt on desktop or a 5 MB prompt on mobile\u2014many developers wrote about these notifications on Stack Overflow. Since around 2015, Firefox has changed its quota approach significantly. Likewise, Safari used to limit data more aggressively on older iOS versions. Some older tutorials suggest comparing IndexedDB to localStorage, but modern browsers allow far larger and more flexible storage with IndexedDB than the old localStorage or cookie-based setups."]}),"\n",(0,i.jsx)(s.hr,{}),"\n",(0,i.jsx)(s.h2,{id:"checking-your-current-indexeddb-usage",children:"Checking Your Current IndexedDB Usage"}),"\n",(0,i.jsxs)(s.p,{children:["To assess where your app stands relative to these storage limits, you can use the ",(0,i.jsx)(s.strong,{children:"Storage Estimation API"}),". The snippet below shows how to estimate both your used storage and the total space allocated to your origin:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" quota"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" navigator"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".estimate"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" totalSpace"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" quota"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".quota;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" usedSpace"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" quota"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".usage;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Approx total allocated space:'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" totalSpace);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Approx used space:'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" usedSpace);"})]})]})})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist#browser_compatibility",children:"Some browsers (all modern ones)"})," also provide a ",(0,i.jsx)(s.code,{children:"navigator.storage.persist()"})," method to request persistent storage, preventing the browser from automatically clearing your data if the user\u2019s device runs low on space. Note that users might deny such requests, or the request might fail silently on stricter environments. Always handle these outcomes gracefully and design your app to degrade if persistent storage is unavailable."]}),"\n",(0,i.jsx)(s.h2,{id:"testing-your-apps-indexeddb-quotas",children:"Testing Your App\u2019s IndexedDB Quotas"}),"\n",(0,i.jsx)(s.p,{children:"The best way to handle real-world usage is to test for low storage conditions and large data sets in different environments. You can fill up the space manually by writing repetitive test data or running scripts that bulk-insert documents until an error occurs."}),"\n",(0,i.jsxs)(s.p,{children:["Real-time usage monitors or dashboards can keep track of your ",(0,i.jsx)(s.code,{children:"navigator.storage.estimate()"})," results, letting you see how close you are to the max limit in production. Developer tools in Chrome or Firefox can simulate limited storage situations, which is crucial for QA:"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)(a.N,{videoId:"Nf37yutU8y4",title:"Simulate low storage quota with DevTools ",duration:"0:42"})}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsx)(s.p,{children:"This short tutorial shows how you can artificially reduce available storage in Google Chrome\u2019s dev tools to see how your app behaves when nearing or exceeding the quota."}),"\n",(0,i.jsx)(s.h2,{id:"handling-errors-when-limits-are-reached",children:"Handling Errors When Limits Are Reached"}),"\n",(0,i.jsxs)(s.p,{children:["When the user\u2019s device is too full or your app exceeds the allotted quota, most browsers will throw a ",(0,i.jsx)(s.strong,{children:"QuotaExceededError"})," (or similarly named exception) when trying to store additional data. Often, the request to IndexedDB simply fails with an error event. Handling this gracefully is essential to avoid crashes or data corruption."]}),"\n",(0,i.jsxs)(s.p,{children:["A typical approach is to wrap your write operations in try/catch blocks or in ",(0,i.jsx)(s.code,{children:"onsuccess"})," / ",(0,i.jsx)(s.code,{children:"onerror"})," event callbacks. If you detect a quota error, you can prompt the user to clear out old items or reduce the scope of offline data. Some apps implement a fallback system that removes less critical documents to free space and then retries the write."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"try"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" tx"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".transaction"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'largeStore'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'readwrite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" store"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" tx"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".objectStore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'largeStore'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" store"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".add"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(hugeData"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" someKey);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" tx"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".done;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"catch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (error) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"error"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".name "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'QuotaExceededError'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".warn"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'IndexedDB quota exceeded. Cleanup or prompt user to free space.'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Optionally remove older data or show a UI hint:"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // removeOldDocuments();"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // displayStorageFullDialog();"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"else"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // handle other errors"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".error"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'IndexedDB write error:'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" error);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"tricks-to-exceed-the-storage-size-limitation",children:"Tricks to Exceed the Storage Size Limitation"}),"\n",(0,i.jsx)(s.p,{children:"Even if you plan well, your app might need more storage than a single origin typically allows. There are a few advanced tactics you can use:"}),"\n",(0,i.jsxs)(s.p,{children:["If you store binary data such as images or videos, consider compressing them via the Compression Streams API. For textual or ",(0,i.jsx)(s.a,{href:"/articles/json-based-database.html",children:"JSON data"}),", a library like ",(0,i.jsx)(s.a,{href:"/",children:"RxDB"})," supports built-in ",(0,i.jsx)(s.a,{href:"/key-compression.html",children:"key-compression"})," to shorten field names or entire documents. This can be extremely helpful when storing large sets of objects:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Example: How key-compression can transform your documents internally"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" uncompressed"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "firstName"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Corrine"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "lastName"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Ziemann"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "shoppingCartItems"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "productNumber"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 29857"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "amount"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "productNumber"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 53409"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "amount"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 6"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" compressed"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|e"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Corrine"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|g"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Ziemann"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|i"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|h"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 29857"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|b"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|h"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 53409"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|b"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 6"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Sharding data across multiple subdomains or iframes is another trick, though it complicates communication. When you need truly massive offline data, you might store part of the data under ",(0,i.jsx)(s.code,{children:"sub1.yoursite.com"})," and another chunk under ",(0,i.jsx)(s.code,{children:"sub2.yoursite.com"}),", using ",(0,i.jsx)(s.code,{children:"postMessage()"})," to coordinate. This can circumvent single-origin limitations, but it introduces extra complexity. Another effective method is to let data expire automatically\u2014perhaps older records are removed if they haven\u2019t been accessed for a certain period."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"indexeddb-max-size-of-a-single-object",children:"IndexedDB Max Size of a Single Object"}),"\n",(0,i.jsxs)(s.p,{children:["There is no explicit cap on how large an individual object or record in IndexedDB can be, other than the overall disk quota. If you attempt to store one extremely large object, you will eventually hit browser memory constraints or the global storage quota. In practice, you\u2019ll encounter out-of-memory issues in JavaScript before IndexedDB itself refuses a single large write. A helpful test can be seen in ",(0,i.jsx)(s.a,{href:"https://jsfiddle.net/sdrqf8om/2/",children:"this JSFiddle experiment"})," where you see browsers can crash when creating massive in-memory objects."]}),"\n",(0,i.jsx)(s.h2,{id:"is-there-a-time-limit-for-data-stored-in-indexeddb",children:"Is There a Time Limit for Data Stored in IndexedDB?"}),"\n",(0,i.jsx)(s.p,{children:"IndexedDB data can remain indefinitely as long as the user does not clear the browser\u2019s data or the origin does not run afoul of automated eviction policies (e.g., Safari or Android might remove large caches for sites unused over a long period when space is needed). Typically, there is no \u201ctime limit,\u201d but ephemeral modes or incognito sessions have their own rules. If you rely on permanent offline data, request persistent storage and handle the possibility that the user or the OS could still remove your data under extreme conditions. Especially Safari is known to be very fast in deleting local data."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"/files/safari-database.png",alt:"safari database",width:"200"})}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.p,{children:["Learn more by checking the ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API",children:"IndexedDB official docs"}),", which detail store design, error handling, and quota usage. If you need a straightforward way to manage large offline data with compression and conflict resolution, explore the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),". You can also join the community on ",(0,i.jsx)(s.a,{href:"/code/",children:"GitHub"})," to share tips on overcoming the ",(0,i.jsx)(s.strong,{children:"IndexedDB max storage size limit"})," in production environments."]})]})}function p(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>t});var n=r(6540);const i={},o=n.createContext(i);function a(e){const s=n.useContext(o);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),n.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/2c41656d.5238f25d.js b/docs/assets/js/2c41656d.5238f25d.js
deleted file mode 100644
index af785fc5bee..00000000000
--- a/docs/assets/js/2c41656d.5238f25d.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4142],{8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}},9376(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/react-indexeddb","title":"IndexedDB Database in React Apps - The Power of RxDB","description":"Discover how RxDB simplifies IndexedDB in React, offering reactive queries, offline-first capability, encryption, compression, and effortless integration.","source":"@site/docs/articles/react-indexeddb.md","sourceDirName":"articles","slug":"/articles/react-indexeddb.html","permalink":"/articles/react-indexeddb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"IndexedDB Database in React Apps - The Power of RxDB","slug":"react-indexeddb.html","description":"Discover how RxDB simplifies IndexedDB in React, offering reactive queries, offline-first capability, encryption, compression, and effortless integration."},"sidebar":"tutorialSidebar","previous":{"title":"Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone","permalink":"/articles/angular-indexeddb.html"},"next":{"title":"Capacitor Database Guide - SQLite, RxDB & More","permalink":"/capacitor-database.html"}}');var i=n(4848),o=n(8453);const a={title:"IndexedDB Database in React Apps - The Power of RxDB",slug:"react-indexeddb.html",description:"Discover how RxDB simplifies IndexedDB in React, offering reactive queries, offline-first capability, encryption, compression, and effortless integration."},l="IndexedDB Database in React Apps - The Power of RxDB",t={},c=[{value:"What is IndexedDB?",id:"what-is-indexeddb",level:2},{value:"Why Use IndexedDB in React",id:"why-use-indexeddb-in-react",level:2},{value:"Why To Not Use Plain IndexedDB",id:"why-to-not-use-plain-indexeddb",level:2},{value:"Set up RxDB in React",id:"set-up-rxdb-in-react",level:2},{value:"Installing RxDB",id:"installing-rxdb",level:3},{value:"Create a Database and Collections",id:"create-a-database-and-collections",level:3},{value:"CRUD Operations",id:"crud-operations",level:3},{value:"Reactive Queries and Live Updates",id:"reactive-queries-and-live-updates",level:2},{value:"With RxJS Observables and React Hooks",id:"with-rxjs-observables-and-react-hooks",level:3},{value:"With Preact Signals",id:"with-preact-signals",level:3},{value:"React IndexedDB Example with RxDB",id:"react-indexeddb-example-with-rxdb",level:2},{value:"Advanced RxDB Features",id:"advanced-rxdb-features",level:2},{value:"Limitations of IndexedDB",id:"limitations-of-indexeddb",level:2},{value:"Alternatives to IndexedDB",id:"alternatives-to-indexeddb",level:2},{value:"Performance comparison with other browser storages",id:"performance-comparison-with-other-browser-storages",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"indexeddb-database-in-react-apps---the-power-of-rxdb",children:"IndexedDB Database in React Apps - The Power of RxDB"})}),"\n",(0,i.jsxs)(s.p,{children:["Building robust, ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"offline-capable"})," React applications often involves leveraging browser storage solutions to manage data. IndexedDB is one such powerful tool, but its raw API can be challenging to work with directly. RxDB abstracts away much of IndexedDB's complexity, providing a more developer-friendly experience. In this article, we'll explore what IndexedDB is, why it's beneficial in React applications, the challenges of using plain IndexedDB, and how ",(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," can simplify your development process while adding advanced features."]}),"\n",(0,i.jsx)(s.h2,{id:"what-is-indexeddb",children:"What is IndexedDB?"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API",children:"IndexedDB"})," is a low-level API for storing significant amounts of structured data in the browser. It provides a transactional database system that can store key-value pairs, complex objects, and more. This storage engine is asynchronous and supports advanced data types, making it suitable for offline storage and complex web applications."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("img",{src:"../files/icons/react.svg",alt:"React IndexedDB",width:"120"})}),"\n",(0,i.jsx)(s.h2,{id:"why-use-indexeddb-in-react",children:"Why Use IndexedDB in React"}),"\n",(0,i.jsx)(s.p,{children:"When building React applications, IndexedDB can play a crucial role in enhancing both performance and user experience. Here are some reasons to consider using IndexedDB:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline-First / Local-First"}),": By storing data locally, your application remains functional even without an internet connection."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Performance"}),": Using local data means ",(0,i.jsx)(s.a,{href:"/articles/zero-latency-local-first.html",children:"zero latency"})," and no loading spinners, as data doesn't need to be fetched over a network."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Easier Implementation"}),": Replicating all data to the client once is often simpler than implementing multiple endpoints for each user interaction."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Scalability"}),": Local data reduces server load because queries run on the client side, decreasing server bandwidth and processing requirements."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"why-to-not-use-plain-indexeddb",children:"Why To Not Use Plain IndexedDB"}),"\n",(0,i.jsx)(s.p,{children:"While IndexedDB itself is powerful, its native API comes with several drawbacks for everyday application developers:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Callback-Based API"}),": IndexedDB was designed with callbacks rather than modern Promises, making asynchronous code more cumbersome."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Complexity"}),": IndexedDB is low-level, intended for library developers rather than for app developers who simply want to store data."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Basic Query API"}),": Its rudimentary query capabilities limit how you can efficiently perform complex queries, whereas libraries like RxDB offer more advanced query features."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"TypeScript Support"}),": Ensuring good TypeScript support with IndexedDB is challenging, especially when trying to enforce document type consistency."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Lack of Observable API"}),": IndexedDB doesn't provide an observable API out of the box. RxDB solves this by enabling you to observe query results or specific document fields."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Cross-Tab Communication"}),": Managing cross-tab updates in plain IndexedDB is difficult. RxDB handles this seamlessly-changes in one tab automatically affect observed data in others."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Missing Advanced Features"}),": Features like encryption or compression aren't built into IndexedDB, but they are available via RxDB."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Limited Platform Support"}),": IndexedDB exists only in the browser. In contrast, RxDB offers swappable storages to use the same code in React Native, Capacitor, or Electron."]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"set-up-rxdb-in-react",children:"Set up RxDB in React"}),"\n",(0,i.jsx)(s.p,{children:"Setting up RxDB with React is straightforward. It abstracts IndexedDB complexities and adds a layer of powerful features over it."}),"\n",(0,i.jsx)(s.h3,{id:"installing-rxdb",children:"Installing RxDB"}),"\n",(0,i.jsx)(s.p,{children:"First, install RxDB and RxJS from npm:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" --save"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"```"})]})})})}),"\n",(0,i.jsx)(s.h3,{id:"create-a-database-and-collections",children:"Create a Database and Collections"}),"\n",(0,i.jsx)(s.p,{children:"RxDB provides two main storage options:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["The free ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"localstorage-based storage"})]}),"\n",(0,i.jsxs)(s.li,{children:["The premium plain ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB-based storage"}),", offering faster performance\nBelow is an example of setting up a simple RxDB ",(0,i.jsx)(s.a,{href:"/articles/react-database.html",children:"database"})," using the localstorage-based storage in a React app:"]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create a database"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // the name of the database"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Define your schema"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'hero schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" description"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Describes a hero in your app'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// add collections"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroSchema"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"crud-operations",children:"CRUD Operations"}),"\n",(0,i.jsx)(s.p,{children:"Once your database is initialized, you can perform all CRUD operations:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Iron Man'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Genius-level intellect'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// bulk insert"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".bulkInsert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Thor'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'God of Thunder'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Hulk'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Superhuman Strength'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]);"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// find and findOne"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" ironMan"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Iron Man'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// update"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Hulk'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".update"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ $set"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Unlimited Strength'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } });"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// delete"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Thor'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"reactive-queries-and-live-updates",children:"Reactive Queries and Live Updates"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB excels in providing reactive data capabilities, ideal for ",(0,i.jsx)(s.a,{href:"/articles/realtime-database.html",children:"real-time applications"}),". There are two main approaches to achieving live queries with RxDB: using RxJS Observables with React Hooks or utilizing Preact Signals."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/animations/realtime.gif",alt:"realtime ui updates",width:"700"})}),"\n",(0,i.jsx)(s.h3,{id:"with-rxjs-observables-and-react-hooks",children:"With RxJS Observables and React Hooks"}),"\n",(0,i.jsx)(s.p,{children:"RxDB integrates seamlessly with RxJS Observables, allowing you to build reactive components. Here's an example of a React component that subscribes to live data updates:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { useState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" useEffect } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'react'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" HeroList"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ collection }) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" setHeroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"([]);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useEffect"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // create an observable query"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" subscription"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(newHeroes "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" setHeroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(newHeroes);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" subscription"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".unsubscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [collection]);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"div"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"h2"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">Hero List"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"h2"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {heroes.map(hero "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"li key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{hero.id}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"strong"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">{hero.name}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"strong"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" -"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {hero.power}"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ))}"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"div"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"This component subscribes to the collection's changes, updating the UI automatically whenever the underlying data changes, even across browser tabs."}),"\n",(0,i.jsx)(s.h3,{id:"with-preact-signals",children:"With Preact Signals"}),"\n",(0,i.jsx)(s.p,{children:"RxDB also supports Preact Signals for reactivity, which can be integrated into React applications. Preact Signals offer a modern, fine-grained reactivity model."}),"\n",(0,i.jsx)(s.p,{children:"First, install the necessary package:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" @preact/signals-core"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" --save"})]})})})}),"\n",(0,i.jsx)(s.p,{children:"Set up RxDB with Preact Signals reactivity:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { PreactSignalsRxReactivityFactory } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/reactivity-preact-signals'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" reactivity"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" PreactSignalsRxReactivityFactory"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Now, you can obtain signals directly from RxDB queries using the double-dollar sign (",(0,i.jsx)(s.code,{children:"$$"}),"):"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" HeroList"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ collection }) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {heroes.map(hero "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"li key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{hero.id}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{hero.name}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ))}"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"This approach provides automatic updates whenever the data changes, without needing to manage subscriptions manually."}),"\n",(0,i.jsx)(s.h2,{id:"react-indexeddb-example-with-rxdb",children:"React IndexedDB Example with RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["A comprehensive example of using RxDB within a React application can be found in the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/react",children:"RxDB GitHub repository"}),". This repository contains sample applications, showcasing best practices and demonstrating how to integrate RxDB for various use cases."]}),"\n",(0,i.jsx)(s.h2,{id:"advanced-rxdb-features",children:"Advanced RxDB Features"}),"\n",(0,i.jsx)(s.p,{children:"RxDB offers many advanced features that extend beyond basic data storage:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"RxDB Replication"}),": Synchronize local data with remote databases seamlessly. Learn more: ",(0,i.jsx)(s.a,{href:"https://rxdb.info/replication.html",children:"RxDB Replication"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Data Migration"}),": Handle schema changes gracefully with automatic data migrations. See: ",(0,i.jsx)(s.a,{href:"https://rxdb.info/migration-schema.html",children:"Data migration"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Encryption"}),": Secure your data with built-in encryption capabilities. Explore: ",(0,i.jsx)(s.a,{href:"https://rxdb.info/encryption.html",children:"Encryption"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Compression"}),": Optimize storage using key compression. Details: ",(0,i.jsx)(s.a,{href:"https://rxdb.info/key-compression.html",children:"Compression"})]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"limitations-of-indexeddb",children:"Limitations of IndexedDB"}),"\n",(0,i.jsx)(s.p,{children:"While IndexedDB is powerful, it has some inherent limitations:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Performance"}),": IndexedDB can be slow under certain conditions. Read more: ",(0,i.jsx)(s.a,{href:"https://rxdb.info/slow-indexeddb.html",children:"Slow IndexedDB"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Storage Limits"}),": Browsers ",(0,i.jsx)(s.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"impose limits"})," on how much data can be stored. See: ",(0,i.jsx)(s.a,{href:"https://rxdb.info/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html",children:"Browser storage limits"})]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"alternatives-to-indexeddb",children:"Alternatives to IndexedDB"}),"\n",(0,i.jsxs)(s.p,{children:["Depending on your application's requirements, there are ",(0,i.jsx)(s.a,{href:"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html",children:"alternative storage solutions"})," to consider:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Origin Private File System (OPFS)"}),": A newer API that can offer better performance. RxDB supports OPFS as well. More info: ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"RxDB OPFS Storage"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"SQLite"}),": Ideal for React applications on Capacitor or ",(0,i.jsx)(s.a,{href:"/articles/ionic-storage.html",children:"Ionic"}),", offering native performance. Explore: ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"RxDB SQLite Storage"})]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"performance-comparison-with-other-browser-storages",children:"Performance comparison with other browser storages"}),"\n",(0,i.jsxs)(s.p,{children:["Here is a ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance overview"})," of the various browser based storage implementation of RxDB:"]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"700"})}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Learn how to use RxDB with the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"})," for a guided introduction."]}),"\n",(0,i.jsxs)(s.li,{children:["Check out the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB GitHub repository"})," and leave a star \u2b50 if you find it useful."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"By leveraging RxDB on top of IndexedDB, you can create highly responsive, offline-capable React applications without dealing with the low-level complexities of IndexedDB directly. With reactive queries, seamless cross-tab communication, and powerful advanced features, RxDB becomes an invaluable tool in modern web development."})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/2efd0200.eec55c75.js b/docs/assets/js/2efd0200.eec55c75.js
deleted file mode 100644
index 23faec4b631..00000000000
--- a/docs/assets/js/2efd0200.eec55c75.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4132],{3247(s,e,n){n.d(e,{g:()=>o});var r=n(4848);function o(s){const e=[];let n=null;return s.children.forEach(s=>{s.props.id?(n&&e.push(n),n={headline:s,paragraphs:[]}):n&&n.paragraphs.push(s)}),n&&e.push(n),(0,r.jsx)("div",{style:i.stepsContainer,children:e.map((s,e)=>(0,r.jsxs)("div",{style:i.stepWrapper,children:[(0,r.jsxs)("div",{style:i.stepIndicator,children:[(0,r.jsx)("div",{style:i.stepNumber,children:e+1}),(0,r.jsx)("div",{style:i.verticalLine})]}),(0,r.jsxs)("div",{style:i.stepContent,children:[(0,r.jsx)("div",{children:s.headline}),s.paragraphs.map((s,e)=>(0,r.jsx)("div",{style:i.item,children:s},e))]})]},e))})}const i={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5179(s,e,n){n.r(e),n.d(e,{assets:()=>c,contentTitle:()=>t,default:()=>p,frontMatter:()=>a,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"tutorials/typescript","title":"TypeScript Setup","description":"Use RxDB with TypeScript to define typed schemas, create typed collections, and build fully typed ORM methods. A quick step-by-step guide.","source":"@site/docs/tutorials/typescript.md","sourceDirName":"tutorials","slug":"/tutorials/typescript.html","permalink":"/tutorials/typescript.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"TypeScript Setup","slug":"typescript.html","description":"Use RxDB with TypeScript to define typed schemas, create typed collections, and build fully typed ORM methods. A quick step-by-step guide."},"sidebar":"tutorialSidebar","previous":{"title":"Development Mode","permalink":"/dev-mode.html"},"next":{"title":"RxDatabase","permalink":"/rx-database.html"}}');var o=n(4848),i=n(8453),l=n(3247);const a={title:"TypeScript Setup",slug:"typescript.html",description:"Use RxDB with TypeScript to define typed schemas, create typed collections, and build fully typed ORM methods. A quick step-by-step guide."},t="Using RxDB with TypeScript",c={},d=[{value:"Declare the types",id:"declare-the-types",level:2},{value:"Create the base document type",id:"create-the-base-document-type",level:2},{value:"Types for the ORM methods",id:"types-for-the-orm-methods",level:2},{value:"Create RxDocument Type",id:"create-rxdocument-type",level:2},{value:"Create RxCollection Type",id:"create-rxcollection-type",level:2},{value:"Create RxDatabase Type",id:"create-rxdatabase-type",level:2},{value:"Using the types",id:"using-the-types",level:2}];function h(s){const e={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...s.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(e.header,{children:(0,o.jsx)(e.h1,{id:"using-rxdb-with-typescript",children:"Using RxDB with TypeScript"})}),"\n",(0,o.jsx)(e.p,{children:"In this tutorial you will learn how to use RxDB with TypeScript.\nWe will create a basic database with one collection and several ORM-methods, fully typed!"}),"\n",(0,o.jsx)(e.p,{children:"RxDB directly comes with its typings and you do not have to install anything else, however the latest version of RxDB requires that you are using Typescript v3.8 or newer.\nOur way to go is"}),"\n",(0,o.jsxs)(e.ul,{children:["\n",(0,o.jsx)(e.li,{children:"First define what the documents look like"}),"\n",(0,o.jsx)(e.li,{children:"Then define what the collections look like"}),"\n",(0,o.jsx)(e.li,{children:"Then define what the database looks like"}),"\n"]}),"\n",(0,o.jsxs)(l.g,{children:[(0,o.jsx)(e.h2,{id:"declare-the-types",children:"Declare the types"}),(0,o.jsx)(e.p,{children:"First you import the types from RxDB."}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" RxDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" RxCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" RxJsonSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" RxDocument"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,o.jsx)(e.h2,{id:"create-the-base-document-type",children:"Create the base document type"}),(0,o.jsx)(e.p,{children:"First we have to define the TypeScript type of the documents of a collection:"}),(0,o.jsxs)(e.p,{children:[(0,o.jsx)(e.strong,{children:"Option A"}),": Create the document type from the schema"]}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" toTypedRxJsonSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ExtractDocumentTypeFromTypedRxJsonSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" RxJsonSchema"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" heroSchemaLiteral"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'hero schema'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" description"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'describes a human being'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" keyCompression"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'passportId'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" passportId"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have set maxLength"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'integer'"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'firstName'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'lastName'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'passportId'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" indexes"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'firstName'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"; "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// <- It is important to set 'as const' to preserve the literal type"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" schemaTyped"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" toTypedRxJsonSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(heroSchemaLiteral);"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// aggregate the document type from the schema"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocType"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" ExtractDocumentTypeFromTypedRxJsonSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"typeof"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" schemaTyped>;"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// create the typed RxJsonSchema from the literal typed object."})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" heroSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" RxJsonSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"HeroDocType"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"> "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" heroSchemaLiteral;"})]})]})})}),(0,o.jsxs)(e.p,{children:[(0,o.jsx)(e.strong,{children:"Option B"}),": Manually type the document type"]}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocType"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" passportId"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"?:"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" number"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"; "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// optional"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),(0,o.jsxs)(e.p,{children:[(0,o.jsx)(e.strong,{children:"Option C"}),": Generate the document type from schema during build time"]}),(0,o.jsxs)(e.p,{children:["If your schema is in a ",(0,o.jsx)(e.code,{children:".json"})," file or generated from somewhere else, you might generate the typings with the ",(0,o.jsx)(e.a,{href:"https://www.npmjs.com/package/json-schema-to-typescript",children:"json-schema-to-typescript"})," module."]}),(0,o.jsx)(e.h2,{id:"types-for-the-orm-methods",children:"Types for the ORM methods"}),(0,o.jsx)(e.p,{children:"We also add some ORM-methods for the document."}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" scream"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" (v"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),(0,o.jsx)(e.h2,{id:"create-rxdocument-type",children:"Create RxDocument Type"}),(0,o.jsx)(e.p,{children:"We can merge these into our HeroDocument."}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsx)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocument"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" RxDocument"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"HeroDocType"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:">;"})]})})})}),(0,o.jsx)(e.h2,{id:"create-rxcollection-type",children:"Create RxCollection Type"}),(0,o.jsx)(e.p,{children:"Now we can define type for the collection which contains the documents."}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// we declare one static ORM-method for the collection"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCollectionMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" countAllDocuments"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" Promise"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"number"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:">;"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// and then merge all our types"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" RxCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocType"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCollectionMethods"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:">;"})})]})})}),(0,o.jsx)(e.h2,{id:"create-rxdatabase-type",children:"Create RxDatabase Type"}),(0,o.jsx)(e.p,{children:"Before we can define the database, we make a helper-type which contains all collections of it."}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" MyDatabaseCollections"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCollection"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),(0,o.jsx)(e.p,{children:"Now the database."}),(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsx)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" MyDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" RxDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"MyDatabaseCollections"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:">;"})]})})})})]}),"\n",(0,o.jsx)(e.h2,{id:"using-the-types",children:"Using the types"}),"\n",(0,o.jsx)(e.p,{children:"Now that we have declare all our types, we can use them."}),"\n",(0,o.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(e.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" * create database and collections"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" MyDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"MyDatabaseCollections"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:">({"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" heroSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" RxJsonSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"HeroDocType"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"> "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human schema'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" description"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'describes a human being'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" keyCompression"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'passportId'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" passportId"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'integer'"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'passportId'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'lastName'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" heroDocMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" scream"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocument"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" what"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".firstName "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ' screams: '"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" what"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".toUpperCase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" heroCollectionMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCollectionMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" countAllDocuments"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" allDocs"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" allDocs"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" heroSchema"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" methods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" heroDocMethods"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" statics"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" heroCollectionMethods"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// add a postInsert-hook"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".postInsert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" myPostInsertHook"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroCollection"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // own collection is bound to the scope"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" docData"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocType"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // documents data"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" doc"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocument"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // RxDocument"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ) {"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'insert to '"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".name "}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '-collection: '"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".firstName);"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // not async"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" * use the database"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert a document"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" hero"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:" HeroDocument"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" passportId"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myId'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'piotr'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'potter'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// access a property"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"hero"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".firstName);"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// use a orm method"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"hero"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".scream"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'AAH!'"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// use a static orm method from the collection"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" amount"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" number"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".countAllDocuments"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(amount);"})]}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" * clean up"})}),"\n",(0,o.jsx)(e.span,{"data-line":"",children:(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(e.span,{"data-line":"",children:[(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myDatabase"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".close"}),(0,o.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})})]})}function p(s={}){const{wrapper:e}={...(0,i.R)(),...s.components};return e?(0,o.jsx)(e,{...s,children:(0,o.jsx)(h,{...s})}):h(s)}},8453(s,e,n){n.d(e,{R:()=>l,x:()=>a});var r=n(6540);const o={},i=r.createContext(o);function l(s){const e=r.useContext(i);return r.useMemo(function(){return"function"==typeof s?s(e):{...e,...s}},[e,s])}function a(s){let e;return e=s.disableParentContext?"function"==typeof s.components?s.components(o):s.components||o:l(s.components),r.createElement(i.Provider,{value:e},s.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/2fe9ecb2.fc29e504.js b/docs/assets/js/2fe9ecb2.fc29e504.js
deleted file mode 100644
index 6198d77dd89..00000000000
--- a/docs/assets/js/2fe9ecb2.fc29e504.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5101],{8453(e,s,n){n.d(s,{R:()=>o,x:()=>a});var t=n(6540);const r={},i=t.createContext(r);function o(e){const s=t.useContext(i);return t.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function a(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:o(e.components),t.createElement(i.Provider,{value:s},e.children)}},8662(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>a,default:()=>c,frontMatter:()=>o,metadata:()=>t,toc:()=>d});const t=JSON.parse('{"id":"articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm","title":"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite","description":"Compare LocalStorage, IndexedDB, Cookies, OPFS, and WASM-SQLite for web storage, performance, limits, and best practices for modern web apps.","source":"@site/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.md","sourceDirName":"articles","slug":"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html","permalink":"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite","slug":"localstorage-indexeddb-cookies-opfs-sqlite-wasm.html","description":"Compare LocalStorage, IndexedDB, Cookies, OPFS, and WASM-SQLite for web storage, performance, limits, and best practices for modern web apps."},"sidebar":"tutorialSidebar","previous":{"title":"Slow IndexedDB","permalink":"/slow-indexeddb.html"},"next":{"title":"17.0.0","permalink":"/releases/17.0.0.html"}}');var r=n(4848),i=n(8453);const o={title:"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite",slug:"localstorage-indexeddb-cookies-opfs-sqlite-wasm.html",description:"Compare LocalStorage, IndexedDB, Cookies, OPFS, and WASM-SQLite for web storage, performance, limits, and best practices for modern web apps."},a="LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite",l={},d=[{value:"The available Storage APIs in a modern Browser",id:"the-available-storage-apis-in-a-modern-browser",level:2},{value:"What are Cookies",id:"what-are-cookies",level:3},{value:"What is LocalStorage",id:"what-is-localstorage",level:3},{value:"What is IndexedDB",id:"what-is-indexeddb",level:3},{value:"What is OPFS",id:"what-is-opfs",level:3},{value:"What is WASM SQLite",id:"what-is-wasm-sqlite",level:3},{value:"What was WebSQL",id:"what-was-websql",level:3},{value:"Feature Comparison",id:"feature-comparison",level:2},{value:"Storing complex JSON Documents",id:"storing-complex-json-documents",level:3},{value:"Multi-Tab Support",id:"multi-tab-support",level:3},{value:"Indexing Support",id:"indexing-support",level:3},{value:"WebWorker Support",id:"webworker-support",level:3},{value:"Storage Size Limits",id:"storage-size-limits",level:2},{value:"Performance Comparison",id:"performance-comparison",level:2},{value:"Initialization Time",id:"initialization-time",level:3},{value:"Latency of small Writes",id:"latency-of-small-writes",level:3},{value:"Latency of small Reads",id:"latency-of-small-reads",level:3},{value:"Big Bulk Writes",id:"big-bulk-writes",level:3},{value:"Big Bulk Reads",id:"big-bulk-reads",level:3},{value:"Performance Conclusions",id:"performance-conclusions",level:2},{value:"Possible Improvements",id:"possible-improvements",level:2},{value:"Future Improvements",id:"future-improvements",level:2},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const s={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(s.header,{children:(0,r.jsx)(s.h1,{id:"localstorage-vs-indexeddb-vs-cookies-vs-opfs-vs-wasm-sqlite",children:"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite"})}),"\n",(0,r.jsxs)(s.p,{children:["So you are building that web application and you want to ",(0,r.jsx)(s.strong,{children:"store data inside of your users browser"}),". Maybe you just need to store some small flags or you even need a fully fledged database."]}),"\n",(0,r.jsxs)(s.p,{children:["The types of web applications we build have changed significantly. In the early years of the web we served static html files. Then we served dynamically rendered html and later we build ",(0,r.jsx)(s.strong,{children:"single page applications"})," that run most logic on the client. And for the coming years you might want to build so called ",(0,r.jsx)(s.a,{href:"/offline-first.html",children:"local first apps"})," that handle big and complex data operations solely on the client and even work when offline, which gives you the opportunity to build ",(0,r.jsx)(s.strong,{children:"zero-latency"})," user interactions."]}),"\n",(0,r.jsxs)(s.p,{children:["In the early days of the web, ",(0,r.jsx)(s.strong,{children:"cookies"})," were the only option for storing small key-value assignments.. But JavaScript and browsers have evolved significantly and better storage APIs have been added which pave the way for bigger and more complex data operations."]}),"\n",(0,r.jsxs)(s.p,{children:["In this article, we will dive into the various technologies available for storing and querying data in a browser. We'll explore traditional methods like ",(0,r.jsx)(s.strong,{children:"Cookies"}),", ",(0,r.jsx)(s.strong,{children:"localStorage"}),", ",(0,r.jsx)(s.strong,{children:"WebSQL"}),", ",(0,r.jsx)(s.strong,{children:"IndexedDB"})," and newer solutions such as ",(0,r.jsx)(s.strong,{children:"OPFS"})," and ",(0,r.jsx)(s.strong,{children:"SQLite via WebAssembly"}),". We compare the features and limitations and through performance tests we aim to uncover how fast we can write and read data in a web application with the various methods."]}),"\n",(0,r.jsxs)(s.admonition,{type:"note",children:[(0,r.jsxs)(s.p,{children:["You are reading this in the ",(0,r.jsx)(s.a,{href:"/",children:"RxDB"})," docs. RxDB is a JavaScript database that has different storage adapters which can utilize the different storage APIs.\n",(0,r.jsx)(s.strong,{children:"Since 2017"})," I spend most of my time working with these APIs, doing performance tests and building ",(0,r.jsx)(s.a,{href:"/slow-indexeddb.html",children:"hacks"})," and plugins to reach the limits of browser database operation speed."]}),(0,r.jsx)("center",{children:(0,r.jsx)("a",{href:"https://rxdb.info/",children:(0,r.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})})]}),"\n",(0,r.jsx)(s.h2,{id:"the-available-storage-apis-in-a-modern-browser",children:"The available Storage APIs in a modern Browser"}),"\n",(0,r.jsx)(s.p,{children:"First lets have a brief overview of the different APIs, their intentional use case and history:"}),"\n",(0,r.jsx)(s.h3,{id:"what-are-cookies",children:"What are Cookies"}),"\n",(0,r.jsxs)(s.p,{children:["Cookies were first introduced by ",(0,r.jsx)(s.a,{href:"https://www.baekdal.com/thoughts/the-original-cookie-specification-from-1997-was-gdpr-compliant/",children:"netscape in 1994"}),".\nCookies store small pieces of key-value data that are mainly used for session management, personalization, and tracking. Cookies can have several security settings like a time-to-live or the ",(0,r.jsx)(s.code,{children:"domain"})," attribute to share the cookies between several subdomains."]}),"\n",(0,r.jsxs)(s.p,{children:["Cookies values are not only stored at the client but also sent with ",(0,r.jsx)(s.strong,{children:"every http request"})," to the server. This means we cannot store much data in a cookie but it is still interesting how good cookie access performance compared to the other methods. Especially because cookies are such an important base feature of the web, many performance optimizations have been done and even these days there is still progress being made like the ",(0,r.jsx)(s.a,{href:"https://blog.chromium.org/2024/06/introducing-shared-memory-versioning-to.html",children:"Shared Memory Versioning"})," by chromium or the asynchronous ",(0,r.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Cookie_Store_API",children:"CookieStore API"}),"."]}),"\n",(0,r.jsx)(s.h3,{id:"what-is-localstorage",children:"What is LocalStorage"}),"\n",(0,r.jsxs)(s.p,{children:["The ",(0,r.jsx)(s.a,{href:"/articles/localstorage.html",children:"localStorage API"})," was first proposed as part of the ",(0,r.jsx)(s.a,{href:"https://www.w3.org/TR/2009/WD-webstorage-20090423/#the-localstorage-attribute",children:"WebStorage specification in 2009"}),".\nLocalStorage provides a simple API to store key-value pairs inside of a web browser. It has the methods ",(0,r.jsx)(s.code,{children:"setItem"}),", ",(0,r.jsx)(s.code,{children:"getItem"}),", ",(0,r.jsx)(s.code,{children:"removeItem"})," and ",(0,r.jsx)(s.code,{children:"clear"})," which is all you need from a key-value store. LocalStorage is only suitable for storing small amounts of data that need to persist across sessions and it is ",(0,r.jsx)(s.a,{href:"/articles/localstorage.html#understanding-the-limitations-of-local-storage",children:"limited by a 5MB storage cap"}),". Storing complex data is only possible by transforming it into a string for example with ",(0,r.jsx)(s.code,{children:"JSON.stringify()"}),".\nThe API is not asynchronous which means if fully blocks your JavaScript process while doing stuff. Therefore running heavy operations on it might block your UI from rendering."]}),"\n",(0,r.jsxs)(s.blockquote,{children:["\n",(0,r.jsxs)(s.p,{children:["There is also the ",(0,r.jsx)(s.strong,{children:"SessionStorage"})," API. The key difference is that localStorage data persists indefinitely until explicitly cleared, while sessionStorage data is cleared when the browser tab or window is closed."]}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"what-is-indexeddb",children:"What is IndexedDB"}),"\n",(0,r.jsxs)(s.p,{children:['IndexedDB was first introduced as "Indexed Database API" ',(0,r.jsx)(s.a,{href:"https://www.w3.org/TR/IndexedDB/#sotd",children:"in 2015"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," is a low-level API for storing large amounts of structured JSON data. While the API is a bit hard to use, IndexedDB can utilize indexes and asynchronous operations. It lacks support for complex queries and only allows to iterate over the indexes which makes it more like a base layer for other libraries then a fully fledged database."]}),"\n",(0,r.jsxs)(s.p,{children:["In 2018, IndexedDB version 2.0 ",(0,r.jsx)(s.a,{href:"https://hacks.mozilla.org/2016/10/whats-new-in-indexeddb-2-0/",children:"was introduced"}),". This added some major improvements. Most noticeable the ",(0,r.jsx)(s.code,{children:"getAll()"})," method which improves performance dramatically when fetching bulks of JSON documents."]}),"\n",(0,r.jsxs)(s.p,{children:["IndexedDB ",(0,r.jsx)(s.a,{href:"https://w3c.github.io/IndexedDB/",children:"version 3.0"})," is in the workings which contains many improvements. Most important the addition of ",(0,r.jsx)(s.code,{children:"Promise"})," based calls that makes modern JS features like ",(0,r.jsx)(s.code,{children:"async/await"})," more useful."]}),"\n",(0,r.jsx)(s.h3,{id:"what-is-opfs",children:"What is OPFS"}),"\n",(0,r.jsxs)(s.p,{children:["The ",(0,r.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"Origin Private File System"})," (OPFS) is a ",(0,r.jsx)(s.a,{href:"https://caniuse.com/mdn-api_filesystemfilehandle_createsyncaccesshandle",children:"relatively new"})," API that allows web applications to store large files directly in the browser. It is designed for data-intensive applications that want to write and read ",(0,r.jsx)(s.strong,{children:"binary data"})," in a simulated file system."]}),"\n",(0,r.jsx)(s.p,{children:"OPFS can be used in two modes:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["Either asynchronous on the ",(0,r.jsx)(s.a,{href:"/rx-storage-opfs.html#using-opfs-in-the-main-thread-instead-of-a-worker",children:"main thread"})]}),"\n",(0,r.jsxs)(s.li,{children:["Or in a WebWorker with the faster, asynchronous access with the ",(0,r.jsx)(s.code,{children:"createSyncAccessHandle()"})," method."]}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:['Because only binary data can be processed, OPFS is made to be a base filesystem for library developers. You will unlikely directly want to use the OPFS in your code when you build a "normal" application because it is too complex. That would only make sense for storing plain files like images, not to store and query ',(0,r.jsx)(s.a,{href:"/articles/json-based-database.html",children:"JSON data"})," efficiently. I have build a ",(0,r.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS based storage"})," for RxDB with proper indexing and querying and it took me several months."]}),"\n",(0,r.jsx)(s.h3,{id:"what-is-wasm-sqlite",children:"What is WASM SQLite"}),"\n",(0,r.jsx)("center",{children:(0,r.jsx)("img",{src:"../files/icons/sqlite.svg",alt:"WASM SQLite",width:"140",class:"img-padding"})}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.a,{href:"https://webassembly.org/",children:"WebAssembly"})," (Wasm) is a binary format that allows high-performance code execution on the web.\nWasm was added to major browsers over the course of 2017 which opened a wide range of opportunities on what to run inside of a browser. You can compile native libraries to WebAssembly and just run them on the client with just a few adjustments. WASM code can be shipped to browser apps and generally runs much faster compared to JavaScript, but still about ",(0,r.jsx)(s.a,{href:"https://www.usenix.org/conference/atc19/presentation/jangda",children:"10% slower then native"}),"."]}),"\n",(0,r.jsx)(s.p,{children:"Many people started to use compiled SQLite as a database inside of the browser which is why it makes sense to also compare this setup to the native APIs."}),"\n",(0,r.jsxs)(s.p,{children:["The compiled byte code of SQLite has a size of ",(0,r.jsx)(s.a,{href:"https://sqlite.org/download.html",children:"about 938.9\xa0kB"})," which must be downloaded and parsed by the users on the first page load. WASM cannot directly access any persistent storage API in the browser. Instead it requires data to flow from WASM to the main-thread and then can be put into one of the browser APIs. This is done with so called ",(0,r.jsx)(s.a,{href:"https://www.sqlite.org/vfs.html",children:"VFS (virtual file system) adapters"})," that handle data access from SQLite to anything else."]}),"\n",(0,r.jsx)(s.h3,{id:"what-was-websql",children:"What was WebSQL"}),"\n",(0,r.jsxs)(s.p,{children:["WebSQL ",(0,r.jsx)(s.strong,{children:"was"})," a web API ",(0,r.jsx)(s.a,{href:"https://www.w3.org/TR/webdatabase/",children:"introduced in 2009"})," that allowed browsers to use SQL databases for client-side storage, based on SQLite. The idea was to give developers a way to store and query data using SQL on the client side, similar to server-side databases.\nWebSQL has been ",(0,r.jsx)(s.strong,{children:"removed from browsers"})," in the current years for multiple good reasons:"]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"WebSQL was not standardized and having an API based on a single specific implementation in form of the SQLite source code is hard to ever make it to a standard."}),"\n",(0,r.jsxs)(s.li,{children:["WebSQL required browsers to use a ",(0,r.jsx)(s.a,{href:"https://developer.chrome.com/blog/deprecating-web-sql#reasons_for_deprecating_web_sql",children:"specific version"})," of SQLite (version 3.6.19) which means whenever there would be any update or bugfix to SQLite, it would not be possible to add that to WebSQL without possible breaking the web."]}),"\n",(0,r.jsx)(s.li,{children:"Major browsers like firefox never supported WebSQL."}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["Therefore in the following we will ",(0,r.jsx)(s.strong,{children:"just ignore WebSQL"})," even if it would be possible to run tests on in by setting specific browser flags or using old versions of chromium."]}),"\n",(0,r.jsx)(s.hr,{}),"\n",(0,r.jsx)(s.h2,{id:"feature-comparison",children:"Feature Comparison"}),"\n",(0,r.jsx)(s.p,{children:"Now that you know the basic concepts of the APIs, lets compare some specific features that have shown to be important for people using RxDB and browser based storages in general."}),"\n",(0,r.jsx)(s.h3,{id:"storing-complex-json-documents",children:"Storing complex JSON Documents"}),"\n",(0,r.jsxs)(s.p,{children:['When you store data in a web application, most often you want to store complex JSON documents and not only "normal" values like the ',(0,r.jsx)(s.code,{children:"integers"})," and ",(0,r.jsx)(s.code,{children:"strings"})," you store in a server side database."]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"Only IndexedDB works with JSON objects natively."}),"\n",(0,r.jsxs)(s.li,{children:["With SQLite WASM you can ",(0,r.jsx)(s.a,{href:"https://www.sqlite.org/json1.html",children:"store JSON"})," in a ",(0,r.jsx)(s.code,{children:"text"})," column since version 3.38.0 (2022-02-22) and even run deep queries on it and use single attributes as indexes."]}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["Every of the other APIs can only store strings or binary data. Of course you can transform any JSON object to a string with ",(0,r.jsx)(s.code,{children:"JSON.stringify()"})," but not having the JSON support in the API can make things complex when running queries and running ",(0,r.jsx)(s.code,{children:"JSON.stringify()"})," many times can cause performance problems."]}),"\n",(0,r.jsx)(s.h3,{id:"multi-tab-support",children:"Multi-Tab Support"}),"\n",(0,r.jsxs)(s.p,{children:["A big difference when building a Web App compared to ",(0,r.jsx)(s.a,{href:"/electron-database.html",children:"Electron"})," or ",(0,r.jsx)(s.a,{href:"/react-native-database.html",children:"React-Native"}),", is that the user will open and close the app in ",(0,r.jsx)(s.strong,{children:"multiple browser tabs at the same time"}),". Therefore you have not only one JavaScript process running, but many of them can exist and might have to share state changes between each other to not show ",(0,r.jsx)(s.strong,{children:"outdated data"})," to the user."]}),"\n",(0,r.jsxs)(s.blockquote,{children:["\n",(0,r.jsxs)(s.p,{children:["If your users' muscle memory puts the left hand on the ",(0,r.jsx)(s.strong,{children:"F5"})," key while using your website, you did something wrong!"]}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"Not all storage APIs support a way to automatically share write events between tabs."}),"\n",(0,r.jsxs)(s.p,{children:["Only localstorage has a way to automatically share write events between tabs by the API itself with the ",(0,r.jsx)(s.a,{href:"/articles/localstorage.html#localstorage-vs-indexeddb",children:"storage-event"})," which can be used to observe changes."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// localStorage can observe changes with the storage event."})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// This feature is missing in IndexedDB and others"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addEventListener"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"storage"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (event) "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {});"})]})]})})}),"\n",(0,r.jsxs)(s.p,{children:["There was the ",(0,r.jsx)(s.a,{href:"https://stackoverflow.com/a/33270440",children:"experimental IndexedDB observers API"})," for chrome, but the proposal repository has been archived."]}),"\n",(0,r.jsx)(s.p,{children:"To workaround this problem, there are two solutions:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["The first option is to use the ",(0,r.jsx)(s.a,{href:"https://github.com/pubkey/broadcast-channel",children:"BroadcastChannel API"})," which can send messages across browser tabs. So whenever you do a write to the storage, you also send a notification to other tabs to inform them about these changes. This is the most common workaround which is also used by RxDB. Notice that there is also the ",(0,r.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_Locks_API",children:"WebLocks API"})," which can be used to have mutexes across browser tabs."]}),"\n",(0,r.jsxs)(s.li,{children:["The other solution is to use the ",(0,r.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker",children:"SharedWorker"})," and do all writes inside of the worker. All browser tabs can then subscribe to messages from that ",(0,r.jsx)(s.strong,{children:"single"})," SharedWorker and know about changes."]}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"indexing-support",children:"Indexing Support"}),"\n",(0,r.jsxs)(s.p,{children:["The big difference between a database and storing data in a plain file, is that a database is writing data in a format that allows running operations over indexes to facilitate fast performant queries. From our list of technologies only ",(0,r.jsx)(s.strong,{children:"IndexedDB"})," and ",(0,r.jsx)(s.strong,{children:"WASM SQLite"})," support for indexing out of the box. In theory you can build indexes on top of any storage like localstorage or OPFS but you likely should not want to do that by yourself."]}),"\n",(0,r.jsx)(s.p,{children:"In IndexedDB for example, we can fetch a bulk of documents by a given index range:"}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// find all products with a price between 10 and 50"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" keyRange"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" IDBKeyRange"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".bound"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"10"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" transaction"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".transaction"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'products'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'readonly'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" objectStore"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" transaction"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".objectStore"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'products'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" index"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" objectStore"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".index"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'priceIndex'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" request"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" index"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getAll"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(keyRange);"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((res"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" rej) "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" request"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onsuccess"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (event) "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" res"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"event"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"target"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".result);"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" request"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onerror"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (event) "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" rej"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(event);"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,r.jsxs)(s.p,{children:["Notice that IndexedDB has the limitation of ",(0,r.jsx)(s.a,{href:"https://github.com/w3c/IndexedDB/issues/76",children:"not having indexes on boolean values"}),". You can only index strings and numbers. To workaround that you have to transform boolean to numbers and backwards when storing the data."]}),"\n",(0,r.jsx)(s.h3,{id:"webworker-support",children:"WebWorker Support"}),"\n",(0,r.jsxs)(s.p,{children:["When running heavy data operations, you might want to move the processing away from the JavaScript main thread. This ensures that our app keeps being responsive and fast while the processing can run in parallel in the background. In a browser you can either use the ",(0,r.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API",children:"WebWorker"}),", ",(0,r.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker",children:"SharedWorker"})," or the ",(0,r.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API",children:"ServiceWorker"})," API to do that. In RxDB you can use the ",(0,r.jsx)(s.a,{href:"/rx-storage-worker.html",children:"WebWorker"})," or ",(0,r.jsx)(s.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker"})," plugins to move your storage inside of a worker."]}),"\n",(0,r.jsxs)(s.p,{children:["The most common API for that use case is spawning a ",(0,r.jsx)(s.strong,{children:"WebWorker"})," and doing most work on that second JavaScript process. The worker is spawned from a separate JavaScript file (or base64 string) and communicates with the main thread by sending data with ",(0,r.jsx)(s.code,{children:"postMessage()"}),"."]}),"\n",(0,r.jsxs)(s.p,{children:["Unfortunately ",(0,r.jsx)(s.strong,{children:"LocalStorage"})," and ",(0,r.jsx)(s.strong,{children:"Cookies"})," ",(0,r.jsx)(s.a,{href:"https://stackoverflow.com/questions/6179159/accessing-localstorage-from-a-webworker",children:"cannot be used in WebWorker or SharedWorker"})," because of the design and security constraints. WebWorkers run in a separate global context from the main browser thread and therefore cannot do stuff that might impact the main thread. They have no direct access to certain web APIs, like the DOM, localStorage, or cookies."]}),"\n",(0,r.jsxs)(s.p,{children:["Everything else can be used from inside a WebWorker.\nThe fast version of OPFS with the ",(0,r.jsx)(s.code,{children:"createSyncAccessHandle"})," method can ",(0,r.jsx)(s.strong,{children:"only"})," ",(0,r.jsx)(s.a,{href:"/rx-storage-opfs.html#opfs-limitations",children:"be used in a WebWorker"}),", and ",(0,r.jsx)(s.strong,{children:"not on the main thread"}),". This is because all the operations of the returned ",(0,r.jsx)(s.code,{children:"AccessHandle"})," are ",(0,r.jsx)(s.strong,{children:"not async"})," and therefore block the JavaScript process, so you do want to do that on the main thread and block everything."]}),"\n",(0,r.jsx)(s.hr,{}),"\n",(0,r.jsx)(s.h2,{id:"storage-size-limits",children:"Storage Size Limits"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Cookies"})," are limited to about ",(0,r.jsx)(s.code,{children:"4 KB"})," of data in ",(0,r.jsx)(s.a,{href:"https://datatracker.ietf.org/doc/html/rfc6265#section-6.1",children:"RFC-6265"}),". Because the stored cookies are send to the server with every HTTP request, this limitation is reasonable. You can test your browsers cookie limits ",(0,r.jsx)(s.a,{href:"http://www.ruslog.com/tools/cookies.html",children:"here"}),". Notice that you should never fill up the full ",(0,r.jsx)(s.code,{children:"4 KB"})," of your cookies because your web server will not accept too long headers and reject the requests with ",(0,r.jsx)(s.code,{children:"HTTP ERROR 431 - Request header fields too large"}),". Once you have reached that point you can not even serve updated JavaScript to your user to clean up the cookies and you will have locked out that user until the cookies get cleaned up manually."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"LocalStorage"})," has a storage size limitation that varies depending on the browser, but generally ranges from 4 MB to 10 MB per origin. You can test your localStorage size limit ",(0,r.jsx)(s.a,{href:"https://arty.name/localstorage.html",children:"here"}),"."]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"Chrome/Chromium/Edge: 5 MB per domain"}),"\n",(0,r.jsx)(s.li,{children:"Firefox: 10 MB per domain"}),"\n",(0,r.jsx)(s.li,{children:"Safari: 4-5 MB per domain (varies slightly between versions)"}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"IndexedDB"})," does not have a specific fixed size limitation like localStorage. The maximum storage size for IndexedDB depends on the browser implementation. The upper limit is typically based on the available disc space on the user's device. In chromium browsers it can use up to 80% of total disk space. You can get an estimation about the storage size limit by calling ",(0,r.jsx)(s.code,{children:"await navigator.storage.estimate()"}),". Typically you can store gigabytes of data which can be tried out ",(0,r.jsx)(s.a,{href:"https://demo.agektmr.com/storage/",children:"here"}),". Notice that we have a full article about ",(0,r.jsx)(s.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"storage max size limits of IndexedDB"})," that covers this topic."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"OPFS"})," has the same storage size limitation as IndexedDB. Its limit depends on the available disc space. This can also be tested ",(0,r.jsx)(s.a,{href:"https://demo.agektmr.com/storage/",children:"here"}),"."]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(s.hr,{}),"\n",(0,r.jsx)(s.h2,{id:"performance-comparison",children:"Performance Comparison"}),"\n",(0,r.jsx)(s.p,{children:"Now that we've reviewed the features of each storage method, let's dive into performance comparisons, focusing on initialization times, read/write latencies, and bulk operations."}),"\n",(0,r.jsxs)(s.p,{children:["Notice that we only run simple tests and for your specific use case in your application the results might differ. Also we only compare performance in google chrome (version 128.0.6613.137). Firefox and Safari have similar ",(0,r.jsx)(s.strong,{children:"but not equal"})," performance patterns. You can run the test by yourself on your own machine from this ",(0,r.jsx)(s.a,{href:"https://github.com/pubkey/localstorage-indexeddb-cookies-opfs-sqlite-wasm",children:"github repository"}),'. For all tests we throttle the network to behave like the average german internet speed. (download: 135,900 kbit/s, upload: 28,400 kbit/s, latency: 125ms). Also all tests store an "average" JSON object that might be required to be stringified depending on the storage. We also only test the performance of storing documents by id because some of the technologies (cookies, OPFS and localstorage) do not support indexed range operations so it makes no sense to compare the performance of these.']}),"\n",(0,r.jsx)(s.h3,{id:"initialization-time",children:"Initialization Time"}),"\n",(0,r.jsx)(s.p,{children:"Before you can store any data, many APIs require a setup process like creating databases, spawning WebAssembly processes or downloading additional stuff. To ensure your app starts fast, the initialization time is important."}),"\n",(0,r.jsx)(s.p,{children:"The APIs of localStorage and Cookies do not have any setup process and can be directly used. IndexedDB requires to open a database and a store inside of it. WASM SQLite needs to download a WASM file and process it. OPFS needs to download and start a worker file and initialize the virtual file system directory."}),"\n",(0,r.jsx)(s.p,{children:"Here are the time measurements from how long it takes until the first bit of data can be stored:"}),"\n",(0,r.jsxs)(s.table,{children:[(0,r.jsx)(s.thead,{children:(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.th,{children:"Technology"}),(0,r.jsx)(s.th,{children:"Time in Milliseconds"})]})}),(0,r.jsxs)(s.tbody,{children:[(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"IndexedDB"}),(0,r.jsx)(s.td,{children:"46"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS Main Thread"}),(0,r.jsx)(s.td,{children:"23"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS WebWorker"}),(0,r.jsx)(s.td,{children:"26.8"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (memory)"}),(0,r.jsx)(s.td,{children:"504"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (IndexedDB)"}),(0,r.jsx)(s.td,{children:"535"})]})]})]}),"\n",(0,r.jsx)(s.p,{children:"Here we can notice a few things:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"Opening a new IndexedDB database with a single store takes surprisingly long"}),"\n",(0,r.jsx)(s.li,{children:"The latency overhead of sending data from the main thread to a WebWorker OPFS is about 4 milliseconds. Here we only send minimal data to init the OPFS file handler. It will be interesting if that latency increases when more data is processed."}),"\n",(0,r.jsx)(s.li,{children:"Downloading and parsing WASM SQLite and creating a single table takes about half a second. Using also the IndexedDB VFS to store data persistently adds additional 31 milliseconds. Reloading the page with enabled caching and already prepared tables is a bit faster with 420 milliseconds (memory)."}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"latency-of-small-writes",children:"Latency of small Writes"}),"\n",(0,r.jsx)(s.p,{children:"Next lets test the latency of small writes. This is important when you do many small data changes that happen independent from each other. Like when you stream data from a websocket or persist pseudo randomly happening events like mouse movements."}),"\n",(0,r.jsxs)(s.table,{children:[(0,r.jsx)(s.thead,{children:(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.th,{children:"Technology"}),(0,r.jsx)(s.th,{children:"Time in Milliseconds"})]})}),(0,r.jsxs)(s.tbody,{children:[(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"Cookies"}),(0,r.jsx)(s.td,{children:"0.058"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"LocalStorage"}),(0,r.jsx)(s.td,{children:"0.017"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"IndexedDB"}),(0,r.jsx)(s.td,{children:"0.17"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS Main Thread"}),(0,r.jsx)(s.td,{children:"1.46"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS WebWorker"}),(0,r.jsx)(s.td,{children:"1.54"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (memory)"}),(0,r.jsx)(s.td,{children:"0.17"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (IndexedDB)"}),(0,r.jsx)(s.td,{children:"3.17"})]})]})]}),"\n",(0,r.jsx)(s.p,{children:"Here we can notice a few things:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"LocalStorage has the lowest write latency with only 0.017 milliseconds per write."}),"\n",(0,r.jsx)(s.li,{children:"IndexedDB writes are about 10 times slower compared to localStorage."}),"\n",(0,r.jsx)(s.li,{children:"Sending the data to the WASM SQLite process and letting it persist via IndexedDB is slow with over 3 milliseconds per write."}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["The OPFS operations take about 1.5 milliseconds to write the JSON data into one document per file. We can see the sending the data to a webworker first is a bit slower which comes from the overhead of serializing and deserializing the data on both sides.\nIf we would not create on OPFS file per document but instead append everything to a single file, the performance pattern changes significantly. Then the faster file handle from the ",(0,r.jsx)(s.code,{children:"createSyncAccessHandle()"})," only takes about 1 millisecond per write. But this would require to somehow remember at which position the each document is stored. Therefore in our tests we will continue using one file per document."]}),"\n",(0,r.jsx)(s.h3,{id:"latency-of-small-reads",children:"Latency of small Reads"}),"\n",(0,r.jsxs)(s.p,{children:["Now that we have stored some documents, lets measure how long it takes to read single documents by their ",(0,r.jsx)(s.code,{children:"id"}),"."]}),"\n",(0,r.jsxs)(s.table,{children:[(0,r.jsx)(s.thead,{children:(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.th,{children:"Technology"}),(0,r.jsx)(s.th,{children:"Time in Milliseconds"})]})}),(0,r.jsxs)(s.tbody,{children:[(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"Cookies"}),(0,r.jsx)(s.td,{children:"0.132"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"LocalStorage"}),(0,r.jsx)(s.td,{children:"0.0052"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"IndexedDB"}),(0,r.jsx)(s.td,{children:"0.1"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS Main Thread"}),(0,r.jsx)(s.td,{children:"1.28"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS WebWorker"}),(0,r.jsx)(s.td,{children:"1.41"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (memory)"}),(0,r.jsx)(s.td,{children:"0.45"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (IndexedDB)"}),(0,r.jsx)(s.td,{children:"2.93"})]})]})]}),"\n",(0,r.jsx)(s.p,{children:"Here we can notice a few things:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["LocalStorage reads are ",(0,r.jsx)(s.strong,{children:"really really fast"})," with only 0.0052 milliseconds per read."]}),"\n",(0,r.jsx)(s.li,{children:"The other technologies perform reads in a similar speed to their write latency."}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"big-bulk-writes",children:"Big Bulk Writes"}),"\n",(0,r.jsx)(s.p,{children:"As next step, lets do some big bulk operations with 200 documents at once."}),"\n",(0,r.jsxs)(s.table,{children:[(0,r.jsx)(s.thead,{children:(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.th,{children:"Technology"}),(0,r.jsx)(s.th,{children:"Time in Milliseconds"})]})}),(0,r.jsxs)(s.tbody,{children:[(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"Cookies"}),(0,r.jsx)(s.td,{children:"20.6"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"LocalStorage"}),(0,r.jsx)(s.td,{children:"5.79"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"IndexedDB"}),(0,r.jsx)(s.td,{children:"13.41"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS Main Thread"}),(0,r.jsx)(s.td,{children:"280"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS WebWorker"}),(0,r.jsx)(s.td,{children:"104"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (memory)"}),(0,r.jsx)(s.td,{children:"19.1"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (IndexedDB)"}),(0,r.jsx)(s.td,{children:"37.12"})]})]})]}),"\n",(0,r.jsx)(s.p,{children:"Here we can notice a few things:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"Sending the data to a WebWorker and running it via the faster OPFS API is about twice as fast."}),"\n",(0,r.jsx)(s.li,{children:"WASM SQLite performs better on bulk operations compared to its single write latency. This is because sending the data to WASM and backwards is faster if it is done all at once instead of once per document."}),"\n"]}),"\n",(0,r.jsx)(s.h3,{id:"big-bulk-reads",children:"Big Bulk Reads"}),"\n",(0,r.jsx)(s.p,{children:"Now lets read 100 documents in a bulk request."}),"\n",(0,r.jsxs)(s.table,{children:[(0,r.jsx)(s.thead,{children:(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.th,{children:"Technology"}),(0,r.jsx)(s.th,{children:"Time in Milliseconds"})]})}),(0,r.jsxs)(s.tbody,{children:[(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"Cookies"}),(0,r.jsx)(s.td,{children:"6.34"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"LocalStorage"}),(0,r.jsx)(s.td,{children:"0.39"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"IndexedDB"}),(0,r.jsx)(s.td,{children:"4.99"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS Main Thread"}),(0,r.jsx)(s.td,{children:"54.79"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"OPFS WebWorker"}),(0,r.jsx)(s.td,{children:"25.61"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (memory)"}),(0,r.jsx)(s.td,{children:"3.59"})]}),(0,r.jsxs)(s.tr,{children:[(0,r.jsx)(s.td,{children:"WASM SQLite (IndexedDB)"}),(0,r.jsx)(s.td,{children:"5.84 (35ms without cache)"})]})]})]}),"\n",(0,r.jsx)(s.p,{children:"Here we can notice a few things:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["Reading many files in the OPFS webworker is about ",(0,r.jsx)(s.strong,{children:"twice as fast"})," compared to the slower main thread mode."]}),"\n",(0,r.jsxs)(s.li,{children:["WASM SQLite is surprisingly fast. Further inspection has shown that the WASM SQLite process keeps the documents in memory cached which improves the latency when we do reads directly after writes on the same data. When the browser tab is reloaded between the writes and the reads, finding the 100 documents takes about ",(0,r.jsx)(s.strong,{children:"35 milliseconds"})," instead."]}),"\n"]}),"\n",(0,r.jsx)(s.h2,{id:"performance-conclusions",children:"Performance Conclusions"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["LocalStorage is really fast but remember that is has some downsides:","\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"It blocks the main JavaScript process and therefore should not be used for big bulk operations."}),"\n",(0,r.jsx)(s.li,{children:"Only Key-Value assignments are possible, you cannot use it efficiently when you need to do index based range queries on your data."}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["OPFS is way faster when used in the WebWorker with the ",(0,r.jsx)(s.code,{children:"createSyncAccessHandle()"})," method compare to using it directly in the main thread."]}),"\n",(0,r.jsx)(s.li,{children:"SQLite WASM can be fast but you have to initially download the full binary and start it up which takes about half a second. This might not be relevant at all if your app is started up once and the used for a very long time. But for web-apps that are opened and closed in many browser tabs many times, this might be a problem."}),"\n"]}),"\n",(0,r.jsx)(s.hr,{}),"\n",(0,r.jsx)(s.h2,{id:"possible-improvements",children:"Possible Improvements"}),"\n",(0,r.jsx)(s.p,{children:"There is a wide range of possible improvements and performance hacks to speed up the operations."}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["For IndexedDB I have made a list of ",(0,r.jsx)(s.a,{href:"/slow-indexeddb.html",children:"performance hacks here"}),". For example you can do sharding between multiple database and webworkers or use a custom index strategy."]}),"\n",(0,r.jsxs)(s.li,{children:["OPFS is slow in writing one file per document. But you do not have to do that and instead you can store everything at a single file like a normal database would do. This improves performance dramatically like it was done with the RxDB ",(0,r.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"}),"."]}),"\n",(0,r.jsxs)(s.li,{children:["You can mix up the technologies to optimize for multiple scenarios at once. For example in RxDB there is the ",(0,r.jsx)(s.a,{href:"/rx-storage-localstorage-meta-optimizer.html",children:"localstorage meta optimizer"}),' which stores initial metadata in localstorage and "normal" documents inside of IndexedDB. This improves the initial startup time while still having the documents stored in a way to query them efficiently.']}),"\n",(0,r.jsxs)(s.li,{children:["There is the ",(0,r.jsx)(s.a,{href:"/rx-storage-memory-mapped.html",children:"memory-mapped"})," storage plugin in RxDB which maps data directly to memory. Using this in combination with a shared worker can improve pageloads and query time significantly."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.a,{href:"/key-compression.html",children:"Compressing"})," data before storing it might improve the performance for some of the storages."]}),"\n",(0,r.jsxs)(s.li,{children:["Splitting work up between ",(0,r.jsx)(s.a,{href:"/rx-storage-worker.html",children:"multiple WebWorkers"})," via ",(0,r.jsx)(s.a,{href:"/rx-storage-sharding.html",children:"sharding"})," can improve performance by utilizing the whole capacity of your users device."]}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["Here you can see the ",(0,r.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance comparison"})," of various RxDB storage implementations which gives a better view of real world performance:"]}),"\n",(0,r.jsx)("center",{children:(0,r.jsx)("img",{src:"../files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"700"})}),"\n",(0,r.jsx)(s.h2,{id:"future-improvements",children:"Future Improvements"}),"\n",(0,r.jsx)(s.p,{children:"You are reading this in 2024, but the web does not stand still. There is a good chance that browser get enhanced to allow faster and better data operations."}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"Currently there is no way to directly access a persistent storage from inside a WebAssembly process. If this changes in the future, running SQLite (or a similar database) in a browser might be the best option."}),"\n",(0,r.jsxs)(s.li,{children:["Sending data between the main thread and a WebWorker is slow but might be improved in the future. There is a ",(0,r.jsx)(s.a,{href:"https://surma.dev/things/is-postmessage-slow/",children:"good article"})," about why ",(0,r.jsx)(s.code,{children:"postMessage()"})," is slow."]}),"\n",(0,r.jsxs)(s.li,{children:["IndexedDB lately ",(0,r.jsx)(s.a,{href:"https://developer.chrome.com/blog/maximum-idb-performance-with-storage-buckets",children:"got support"})," for storage buckets (chrome only) which might improve performance."]}),"\n"]}),"\n",(0,r.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["Share my ",(0,r.jsx)(s.a,{href:"https://x.com/rxdbjs/status/1846145062847062391",children:"announcement tweet"})," --\x3e"]}),"\n",(0,r.jsxs)(s.li,{children:["Reproduce the benchmarks at the ",(0,r.jsx)(s.a,{href:"https://github.com/pubkey/localstorage-indexeddb-cookies-opfs-sqlite-wasm",children:"github repo"})]}),"\n",(0,r.jsxs)(s.li,{children:["Learn how to use RxDB with the ",(0,r.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"})]}),"\n",(0,r.jsxs)(s.li,{children:["Check out the ",(0,r.jsx)(s.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB github repo"})," and leave a star \u2b50"]}),"\n"]})]})}function c(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,r.jsx)(s,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/32667c41.fb0c28b5.js b/docs/assets/js/32667c41.fb0c28b5.js
deleted file mode 100644
index d4acb3ff74f..00000000000
--- a/docs/assets/js/32667c41.fb0c28b5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8191],{8453(e,n,s){s.d(n,{R:()=>a,x:()=>t});var r=s(6540);const i={},o=r.createContext(i);function a(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:n},e.children)}},9955(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"nosql-performance-tips","title":"RxDB NoSQL Performance Tips","description":"Skyrocket your NoSQL speed with RxDB tips. Learn about bulk writes, optimized queries, and lean plugin usage for peak performance.","source":"@site/docs/nosql-performance-tips.md","sourceDirName":".","slug":"/nosql-performance-tips.html","permalink":"/nosql-performance-tips.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB NoSQL Performance Tips","slug":"nosql-performance-tips.html","description":"Skyrocket your NoSQL speed with RxDB tips. Learn about bulk writes, optimized queries, and lean plugin usage for peak performance."},"sidebar":"tutorialSidebar","previous":{"title":"RxStorage Performance","permalink":"/rx-storage-performance.html"},"next":{"title":"Slow IndexedDB","permalink":"/slow-indexeddb.html"}}');var i=s(4848),o=s(8453);const a={title:"RxDB NoSQL Performance Tips",slug:"nosql-performance-tips.html",description:"Skyrocket your NoSQL speed with RxDB tips. Learn about bulk writes, optimized queries, and lean plugin usage for peak performance."},t="Performance tips for RxDB and other NoSQL databases",l={},c=[{value:"Use bulk operations",id:"use-bulk-operations",level:2},{value:"Help the query planner by adding operators that better restrict the index range",id:"help-the-query-planner-by-adding-operators-that-better-restrict-the-index-range",level:2},{value:"Set a specific index",id:"set-a-specific-index",level:2},{value:"Try different ordering of index fields",id:"try-different-ordering-of-index-fields",level:2},{value:"Make a Query "hot" to reduce load",id:"make-a-query-hot-to-reduce-load",level:2},{value:"Store parts of your document data as attachment",id:"store-parts-of-your-document-data-as-attachment",level:2},{value:"Process queries in a worker process",id:"process-queries-in-a-worker-process",level:2},{value:"Use less plugins and hooks",id:"use-less-plugins-and-hooks",level:2}];function d(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"performance-tips-for-rxdb-and-other-nosql-databases",children:"Performance tips for RxDB and other NoSQL databases"})}),"\n",(0,i.jsx)(n.p,{children:"In this guide, you'll find techniques to improve the performance of RxDB operations and queries. Notice that all your performance optimizations should be done with a correct tracking of the metrics, otherwise you might change stuff into the wrong direction."}),"\n",(0,i.jsx)(n.h2,{id:"use-bulk-operations",children:"Use bulk operations"}),"\n",(0,i.jsx)(n.p,{children:"When you run write operations on multiple documents, make sure you use bulk operations instead of single document operations."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrong \u274c"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"for"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" of"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" dataAr){"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(docData);"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// right \u2714\ufe0f"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".bulkInsert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(dataAr);"})]})]})})}),"\n",(0,i.jsx)(n.h2,{id:"help-the-query-planner-by-adding-operators-that-better-restrict-the-index-range",children:"Help the query planner by adding operators that better restrict the index range"}),"\n",(0,i.jsx)(n.p,{children:"Often on complex queries, RxDB (and other databases) do not pick the optimal index range when querying a result set.\nYou can add additional restrictive operators to ensure the query runs over a smaller index space and has a better performance."}),"\n",(0,i.jsx)(n.p,{children:"Lets see some examples for different query types."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Adding a restrictive operator for an $or query"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * so that it better limits the index space for the time-field."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" orQuery"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $or"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" time"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $gt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1234"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" time"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $eg"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1234"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" user"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $gt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" time: { $gte"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1234"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// <- add restrictive operator"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Adding a restrictive operator for an $regex query"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * so that it better limits the index space for the user-field."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * We know that all matching fields start with 'foo' so we can"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * tell the query to use that as lower constraint for the index."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" regexQuery"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" user"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $regex"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '^foo(.*)0-9$'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // a complex regex with a ^ in the beginning"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gte"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- add restrictive operator"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Adding a restrictive operator for a query on an enum field."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * so that it better limits the index space for the time-field."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" enumQuery"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here lets assume our status field has the enum type ['idle', 'in-progress', 'done']"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * so our restrictive operator can exclude all documents with 'done' as status."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" status"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $in"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'idle'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'in-progress'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'done'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- add restrictive operator on status"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"set-a-specific-index",children:"Set a specific index"}),"\n",(0,i.jsx)(n.p,{children:"Sometime the query planner of the database itself has no chance in picking the best index of the possible given indexes.\nFor queries where performance is very important, you might want to explicitly specify which index must be used."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myQuery"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // explicitly specify index"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" index"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'fieldA'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'fieldB'"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"try-different-ordering-of-index-fields",children:"Try different ordering of index fields"}),"\n",(0,i.jsxs)(n.p,{children:["The order of the fields in a compound index is very important for performance. When optimizing index usage, you should try out different orders on the index fields and measure which runs faster. For that it is very important to run tests on real-world data where the distribution of the data is the same as in production.\nFor example when there is a query on a user collection with an ",(0,i.jsx)(n.code,{children:"age"})," and a ",(0,i.jsx)(n.code,{children:"gender"})," field, it depends if the index ",(0,i.jsx)(n.code,{children:"['gender', 'age']"})," performance better as ",(0,i.jsx)(n.code,{children:"['age', 'gender']"})," based on the distribution of data:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCollection"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .findOne"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" gender"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $eq"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'm'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Because the developer knows that 50% of the documents are 'male',"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * but only 20% are below age 18,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * it makes sense to enforce using the ['gender', 'age'] index to improve performance."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This could not be known by the query planer which might have chosen ['age', 'gender'] instead."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" index"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'gender'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'age'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["Notice that RxDB has the ",(0,i.jsx)(n.a,{href:"/query-optimizer.html",children:"Query Optimizer Plugin"})," that can be used to automatically find the best indexes."]}),"\n",(0,i.jsx)(n.h2,{id:"make-a-query-hot-to-reduce-load",children:'Make a Query "hot" to reduce load'}),"\n",(0,i.jsxs)(n.p,{children:['Having a query where the up-to-date result set is needed more than once, you might want to make the query "hot" by permanently subscribing to it. This ensures that the query result is kept up to date by RxDB ant the ',(0,i.jsx)(n.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce algorithm"})," at any time so that at the moment you need the current results, it has them already."]}),"\n",(0,i.jsx)(n.p,{children:'For example when you use RxDB at Node.js for a webserver, you should use an outer "hot" query instead of running the same query again on every request to a route.'}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrong \u274c"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'/list'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (req"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" res) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".send"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(result));"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// right \u2714\ufe0f"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// <- make it hot"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'/list'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (req"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" res) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".send"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(result));"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"store-parts-of-your-document-data-as-attachment",children:"Store parts of your document data as attachment"}),"\n",(0,i.jsxs)(n.p,{children:["For in-app databases like RxDB, it does not make sense to partially parse the ",(0,i.jsx)(n.code,{children:"JSON"})," of a document. Instead, always the whole document json is parsed and handled. This has a better performance because ",(0,i.jsx)(n.code,{children:"JSON.parse()"})," in JavaScript directly calls a C++ binding which can parse really fast compared to a partial parsing in JavaScript itself. Also by always having the full document, RxDB can de-duplicate memory caches of document across multiple queries."]}),"\n",(0,i.jsxs)(n.p,{children:["The downside is that very very big documents with a complex structure can increase query time significantly. Documents fields with complex that are mostly not in use, can be move into an ",(0,i.jsx)(n.a,{href:"/rx-attachment.html",children:"attachment"}),". This would lead RxDB to not fetch the attachment data each time the document is loaded from disc. Instead only when explicitly asked for."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".putAttachment"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'otherStuff.json'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" data"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createBlob"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"process-queries-in-a-worker-process",children:"Process queries in a worker process"}),"\n",(0,i.jsxs)(n.p,{children:["Moving database storage into a WebWorker can significantly improve performance in web applications that use RxDB or similar NoSQL databases. When database operations are executed in the main JavaScript thread, they can block or slow down the User Interface, especially during heavy or complex data operations. By offloading these operations to a WebWorker, you effectively separate the data processing workload from the UI thread. This means the main thread remains free to handle user interactions and render updates without delay, leading to a smoother and more responsive user experience. Additionally, WebWorkers allow for parallel data processing, which can expedite tasks like querying and indexing. This approach not only enhances UI responsiveness but also optimizes overall application performance by leveraging the multi-threading capabilities of modern browsers.\nWith RxDB you can use the ",(0,i.jsx)(n.a,{href:"/rx-storage-worker.html",children:"Worker"})," and ",(0,i.jsx)(n.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker"})," plugin to move the query processing away from the main thread."]}),"\n",(0,i.jsx)(n.h2,{id:"use-less-plugins-and-hooks",children:"Use less plugins and hooks"}),"\n",(0,i.jsxs)(n.p,{children:["Utilizing fewer ",(0,i.jsx)(n.a,{href:"/middleware.html",children:"hooks"})," and plugins in RxDB or similar NoSQL database systems can lead to markedly better performance. Each additional hook or plugin introduces extra layers of processing and potential overhead, which can cumulatively slow down database operations. These extensions often execute additional code or enforce extra checks with each operation, such as insertions, updates, or deletions. While they can provide valuable functionalities or custom behaviors, their overuse can inadvertently increase the complexity and execution time of basic database operations. By minimizing their use and only employing essential hooks and plugins, the system can operate more efficiently. This streamlined approach reduces the computational burden on each transaction, leading to faster response times and a more efficient overall data handling process, especially critical in high-load or real-time applications where performance is paramount."]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/326aca46.f7338c91.js b/docs/assets/js/326aca46.f7338c91.js
deleted file mode 100644
index 885a3202aff..00000000000
--- a/docs/assets/js/326aca46.f7338c91.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4853],{1748(e,t,n){n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>r,default:()=>d,frontMatter:()=>o,metadata:()=>a,toc:()=>h});const a=JSON.parse('{"id":"offline-first","title":"Local First / Offline First","description":"Local-First software stores data on client devices for seamless offline and online functionality, enhancing user experience and efficiency.","source":"@site/docs/offline-first.md","sourceDirName":".","slug":"/offline-first.html","permalink":"/offline-first.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Local First / Offline First","slug":"offline-first.html","description":"Local-First software stores data on client devices for seamless offline and online functionality, enhancing user experience and efficiency."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB - The Real-Time Database for Node.js","permalink":"/nodejs-database.html"},"next":{"title":"React Native Database - Sync & Store Like a Pro","permalink":"/react-native-database.html"}}');var i=n(4848),s=n(8453);const o={title:"Local First / Offline First",slug:"offline-first.html",description:"Local-First software stores data on client devices for seamless offline and online functionality, enhancing user experience and efficiency."},r="Local First / Offline First",l={},h=[{value:"UX is better without loading spinners",id:"ux-is-better-without-loading-spinners",level:2},{value:"Multi-tab usage just works",id:"multi-tab-usage-just-works",level:2},{value:"Latency is more important than bandwidth",id:"latency-is-more-important-than-bandwidth",level:2},{value:"Realtime comes for free",id:"realtime-comes-for-free",level:2},{value:"Scales with data size, not with the amount of user interaction",id:"scales-with-data-size-not-with-the-amount-of-user-interaction",level:2},{value:"Modern apps have longer runtimes",id:"modern-apps-have-longer-runtimes",level:2},{value:"You might not need REST",id:"you-might-not-need-rest",level:2},{value:"You might not need Redux",id:"you-might-not-need-redux",level:2},{value:"Follow up",id:"follow-up",level:2}];function c(e){const t={a:"a",admonition:"admonition",blockquote:"blockquote",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.header,{children:(0,i.jsx)(t.h1,{id:"local-first--offline-first",children:"Local First / Offline First"})}),"\n",(0,i.jsxs)(t.p,{children:["Local-First (aka offline first) is a software paradigm where the software stores data locally at the clients device and must work as well offline as it does online.\nTo implement this, you have to store data at the client side, so that your application can still access it when the internet goes away.\nThis can be either done with complex caching strategies, or by using an local-first, ",(0,i.jsx)(t.a,{href:"/articles/offline-database.html",children:"offline database"})," (like ",(0,i.jsx)(t.a,{href:"https://rxdb.info",children:"RxDB"}),") that stores the data inside of a local database like ",(0,i.jsx)(t.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," and replicates it from and to the backend in the background. This makes the local database, not the server, the gateway for all persistent changes in application state."]}),"\n",(0,i.jsxs)(t.blockquote,{children:["\n",(0,i.jsx)(t.p,{children:(0,i.jsx)(t.strong,{children:"Offline first is not about having no internet connection"})}),"\n"]}),"\n",(0,i.jsx)(t.admonition,{type:"note",children:(0,i.jsxs)(t.p,{children:["I wrote a follow-up version of offline/first local first about ",(0,i.jsx)(t.a,{href:"/articles/local-first-future.html",children:"Why Local-First Is the Future and what are Its Limitations"})]})}),"\n",(0,i.jsx)(t.p,{children:"While in the past, internet connection was an unstable, things are changing especially for mobile devices.\nMobile networks become better and having no internet becomes less common even in remote locations.\nSo if we did not care about offline first applications in the past, why should we even care now?\nIn the following I will point out why offline first applications are better, not because they support offline usage, but because of other reasons."}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"RxDB",width:"220"})})}),"\n",(0,i.jsx)(t.h2,{id:"ux-is-better-without-loading-spinners",children:"UX is better without loading spinners"}),"\n",(0,i.jsx)(t.p,{children:"In 'normal' web applications, most user interactions like fetching, saving or deleting data, correspond to a request to the backend server. This means that each of these interactions require the user to await the unknown latency to and from a remote server while looking at a loading spinner.\nIn offline-first apps, the operations go directly against the local storage which happens almost instantly. There is no perceptible loading time and so it is not even necessary to implement a loading spinner at all. As soon as the user clicks, the UI represents the new state as if it was already changed in the backend."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/loading-spinner-not-needed.gif",alt:"loading spinner not needed",width:"300"})}),"\n",(0,i.jsx)(t.h2,{id:"multi-tab-usage-just-works",children:"Multi-tab usage just works"}),"\n",(0,i.jsxs)(t.p,{children:["Many, even big websites like amazon, reddit and stack overflow do not handle multi tab usage correctly. When a user has multiple tabs of the website open and does a login on one of these tabs, the state does not change on the other tabs.\nOn offline first applications, there is always exactly one state of the data across all tabs. Offline first databases (like RxDB) store the data inside of IndexedDb and ",(0,i.jsx)(t.strong,{children:"share the state"})," between all tabs of the same origin."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/multiwindow.gif",alt:"RxDB multi tab",width:"450"})}),"\n",(0,i.jsx)(t.h2,{id:"latency-is-more-important-than-bandwidth",children:"Latency is more important than bandwidth"}),"\n",(0,i.jsx)(t.p,{children:"In the past, often the bandwidth was the limiting factor on determining the loading time of an application.\nBut while bandwidth has improved over the years, latency became the limiting factor.\nYou can always increase the bandwidth by setting up more cables or sending more Starlink satellites to space.\nBut reducing the latency is not so easy. It is defined by the physical properties of the transfer medium, the speed of light and the distance to the server. All of these three are hard to optimize."}),"\n",(0,i.jsxs)(t.p,{children:["Offline first application benefit from that because sending the initial state to the client can be done much faster with more bandwidth. And once the data is there, we do no longer have to care about the latency to the backend server because you can run near ",(0,i.jsx)(t.a,{href:"/articles/zero-latency-local-first.html",children:"zero"})," latency queries locally."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/latency-london-san-franzisco.png",alt:"latency london san franzisco",width:"300"})}),"\n",(0,i.jsx)(t.h2,{id:"realtime-comes-for-free",children:"Realtime comes for free"}),"\n",(0,i.jsxs)(t.p,{children:["Most websites lie to their users. They do not lie because they display wrong data, but because they display ",(0,i.jsx)(t.strong,{children:"old data"})," that was loaded from the backend at the time the user opened the site.\nTo overcome this, you could build a realtime website where you create a websocket that streams updates from the backend to the client. This means work. Your client needs to tell the server which page is currently opened and which updates the client is interested to. Then the server can push updates over the websocket and you can update the UI accordingly."]}),"\n",(0,i.jsxs)(t.p,{children:["With offline first applications, you already have a realtime replication with the backend. Most offline first databases provide some concept of changestream or data subscriptions and with ",(0,i.jsx)(t.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB"})," you can even directly subscribe to query results or single fields of documents. This makes it easy to have an always updated UI whenever data on the backend changes."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/animations/realtime.gif",alt:"realtime ui updates",width:"700"})}),"\n",(0,i.jsx)(t.h2,{id:"scales-with-data-size-not-with-the-amount-of-user-interaction",children:"Scales with data size, not with the amount of user interaction"}),"\n",(0,i.jsx)(t.p,{children:"On normal applications, each user interaction can result in multiple requests to the backend server which increase its load.\nThe more users interact with your application, the more backend resources you have to provide."}),"\n",(0,i.jsx)(t.p,{children:"Offline first applications do not scale up with the amount of user actions but instead they scale up with the amount of data.\nOnce that data is transferred to the client, the user can do as many interactions with it as required without connecting to the server."}),"\n",(0,i.jsx)(t.h2,{id:"modern-apps-have-longer-runtimes",children:"Modern apps have longer runtimes"}),"\n",(0,i.jsx)(t.p,{children:"In the past you used websites only for a short time. You open it, perform some action and then close it again. This made the first load time the important metric when evaluating page speed.\nToday web applications have changed and with it the way we use them. Single page applications are opened once and then used over the whole day. Chat apps, email clients, PWAs and hybrid apps. All of these were made to have long runtimes.\nThis makes the time for user interactions more important than the initial loading time. Offline first applications benefit from that because there is often no loading time on user actions while loading the initial state to the client is not that relevant."}),"\n",(0,i.jsx)(t.h2,{id:"you-might-not-need-rest",children:"You might not need REST"}),"\n",(0,i.jsx)(t.p,{children:"On normal web applications, you make different requests for each kind of data interaction.\nFor that you have to define a swagger route, implement a route handler on the backend and create some client code to send or fetch data from that route. The more complex your application becomes, the more REST routes you have to maintain and implement."}),"\n",(0,i.jsxs)(t.p,{children:["With offline first apps, you have a way to hack around all this cumbersome work. You just replicate the whole state from the server to the client. The replication does not only run once, you have a ",(0,i.jsx)(t.strong,{children:"realtime replication"})," and all changes at one side are automatically there on the other side. On the client, you can access every piece of state with a simple database query.\nWhile this of course only works for amounts of data that the client can load and store, it makes implementing prototypes and simple apps much faster."]}),"\n",(0,i.jsx)(t.h2,{id:"you-might-not-need-redux",children:"You might not need Redux"}),"\n",(0,i.jsx)(t.p,{children:"Data is hard, especially for UI applications where many things can happen at the same time.\nThe user is clicking around. Stuff is loaded from the server. All of these things interact with the global state of the app.\nTo manage this complexity it is common to use state management libraries like Redux or MobX. With them, you write all this lasagna code to wrap the mutation of data and to make the UI react to all these changes."}),"\n",(0,i.jsx)(t.p,{children:"On offline first apps, your global state is already there in a single place stored inside of the local database.\nYou do not have to care whether this data came from the UI, another tab, the backend or another device of the same user. You can just make writes to the database and fetch data out of it."}),"\n",(0,i.jsx)(t.h2,{id:"follow-up",children:"Follow up"}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["Learn how to store and query data with RxDB in the ",(0,i.jsx)(t.a,{href:"/quickstart.html",children:"RxDB Quickstart"})]}),"\n",(0,i.jsx)(t.li,{children:(0,i.jsx)(t.a,{href:"/downsides-of-offline-first.html",children:"Downsides of Offline First"})}),"\n",(0,i.jsxs)(t.li,{children:["I wrote a follow-up version of offline/first local first about ",(0,i.jsx)(t.a,{href:"/articles/local-first-future.html",children:"Why Local-First Is the Future and what are Its Limitations"})]}),"\n"]})]})}function d(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}},8453(e,t,n){n.d(t,{R:()=>o,x:()=>r});var a=n(6540);const i={},s=a.createContext(i);function o(e){const t=a.useContext(s);return a.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),a.createElement(s.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/3417a9c7.4cd2a2af.js b/docs/assets/js/3417a9c7.4cd2a2af.js
deleted file mode 100644
index f394b2368ce..00000000000
--- a/docs/assets/js/3417a9c7.4cd2a2af.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5350],{4572(e,s,r){r.r(s),r.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"articles/firestore-alternative","title":"RxDB - Firestore Alternative to Sync with Your Own Backend","description":"Looking for a Firestore alternative? RxDB is a local-first, NoSQL database that syncs seamlessly with any backend, offers rich offline capabilities, advanced conflict resolution, and reduces vendor lock-in.","source":"@site/docs/articles/firestore-alternative.md","sourceDirName":"articles","slug":"/articles/firestore-alternative.html","permalink":"/articles/firestore-alternative.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB - Firestore Alternative to Sync with Your Own Backend","slug":"firestore-alternative.html","description":"Looking for a Firestore alternative? RxDB is a local-first, NoSQL database that syncs seamlessly with any backend, offers rich offline capabilities, advanced conflict resolution, and reduces vendor lock-in."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB as a Database in a jQuery Application","permalink":"/articles/jquery-database.html"},"next":{"title":"RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend","permalink":"/articles/firebase-realtime-database-alternative.html"}}');var i=r(4848),o=r(8453);const a={title:"RxDB - Firestore Alternative to Sync with Your Own Backend",slug:"firestore-alternative.html",description:"Looking for a Firestore alternative? RxDB is a local-first, NoSQL database that syncs seamlessly with any backend, offers rich offline capabilities, advanced conflict resolution, and reduces vendor lock-in."},l="RxDB - The Firestore Alternative That Can Sync with Your Own Backend",t={},c=[{value:"What Makes RxDB a Great Firestore Alternative?",id:"what-makes-rxdb-a-great-firestore-alternative",level:2},{value:"1. Fully Offline-First",id:"1-fully-offline-first",level:3},{value:"2. Freedom to Use Any Backend",id:"2-freedom-to-use-any-backend",level:3},{value:"3. Advanced Conflict Resolution",id:"3-advanced-conflict-resolution",level:3},{value:"4. Reduced Cloud Costs",id:"4-reduced-cloud-costs",level:3},{value:"5. No Limits on Query Features",id:"5-no-limits-on-query-features",level:3},{value:"6. True Offline-Start Support",id:"6-true-offline-start-support",level:3},{value:"7. Cross-Platform: Any JavaScript Runtime",id:"7-cross-platform-any-javascript-runtime",level:3},{value:"How Does RxDB's Sync Work?",id:"how-does-rxdbs-sync-work",level:2},{value:"Getting Started with RxDB as a Firestore Alternative",id:"getting-started-with-rxdb-as-a-firestore-alternative",level:2},{value:"Install RxDB:",id:"install-rxdb",level:3},{value:"Create a Database:",id:"create-a-database",level:3},{value:"Define Collections:",id:"define-collections",level:3},{value:"Sync",id:"sync",level:3},{value:"Example: Start a WebRTC P2P Replication",id:"example-start-a-webrtc-p2p-replication",level:3},{value:"Is RxDB Right for Your Project?",id:"is-rxdb-right-for-your-project",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"rxdb---the-firestore-alternative-that-can-sync-with-your-own-backend",children:"RxDB - The Firestore Alternative That Can Sync with Your Own Backend"})}),"\n",(0,i.jsxs)(s.p,{children:["If you're seeking a ",(0,i.jsx)(s.strong,{children:"Firestore alternative"}),", you're likely looking for a way to:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Avoid vendor lock-in"})," while still enjoying real-time replication."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Reduce cloud usage costs"})," by reading data locally instead of constantly fetching from the server."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Customize"})," how you store, query, and secure your data."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Implement advanced conflict resolution"})," strategies beyond Firestore's last-write-wins approach."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["Enter ",(0,i.jsx)(s.strong,{children:"RxDB"})," (Reactive Database) - a ",(0,i.jsx)(s.a,{href:"/articles/local-first-future.html",children:"local-first"}),", NoSQL database for JavaScript applications that can sync in real time with ",(0,i.jsx)(s.strong,{children:"any"})," backend of your choice. Whether you're tired of the limitations and fees associated with Firebase Cloud Firestore or simply need more flexibility, RxDB might be the Firestore alternative you've been searching for."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"/files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"what-makes-rxdb-a-great-firestore-alternative",children:"What Makes RxDB a Great Firestore Alternative?"}),"\n",(0,i.jsx)(s.p,{children:"Firestore is convenient for many projects, but it does lock you into Google's ecosystem. Below are some of the key advantages you gain by choosing RxDB:"}),"\n",(0,i.jsx)(s.h3,{id:"1-fully-offline-first",children:"1. Fully Offline-First"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB runs directly in your client application (",(0,i.jsx)(s.a,{href:"/articles/browser-database.html",children:"browser"}),", ",(0,i.jsx)(s.a,{href:"/nodejs-database.html",children:"Node.js"}),", ",(0,i.jsx)(s.a,{href:"/electron-database.html",children:"Electron"}),", ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"}),", etc.). Data is stored locally, so your application ",(0,i.jsx)(s.strong,{children:"remains fully functional even when offline"}),". When the device returns online, RxDB's flexible replication protocol synchronizes your local changes with any remote endpoint."]}),"\n",(0,i.jsx)(s.h3,{id:"2-freedom-to-use-any-backend",children:"2. Freedom to Use Any Backend"}),"\n",(0,i.jsx)(s.p,{children:"Unlike Firestore, RxDB doesn't require a proprietary hosting service. You can:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Host your data on your own server (Node.js, Go, Python, etc.)."}),"\n",(0,i.jsxs)(s.li,{children:["Use existing databases like ",(0,i.jsx)(s.a,{href:"/replication-http.html",children:"PostgreSQL"}),", ",(0,i.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB"}),", or ",(0,i.jsx)(s.a,{href:"/replication.html",children:"MongoDB with custom endpoints"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["Implement a ",(0,i.jsx)(s.a,{href:"/replication-graphql.html",children:"custom GraphQL"})," or ",(0,i.jsx)(s.a,{href:"/replication-http.html",children:"REST-based"})," API for syncing."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["This ",(0,i.jsx)(s.strong,{children:"backend-agnostic"})," approach protects you from vendor lock-in. Your application's client-side data storage remains consistent; only your replication logic (or plugin) changes if you switch servers."]}),"\n",(0,i.jsx)(s.h3,{id:"3-advanced-conflict-resolution",children:"3. Advanced Conflict Resolution"}),"\n",(0,i.jsxs)(s.p,{children:["Firestore enforces a ",(0,i.jsx)(s.a,{href:"https://stackoverflow.com/a/47781502/3443137",children:"last-write-wins"})," conflict resolution strategy. This might cause issues if multiple users or devices update the same data in complex ways."]}),"\n",(0,i.jsx)(s.p,{children:"RxDB lets you:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Implement ",(0,i.jsx)(s.strong,{children:"custom conflict resolution"})," via ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html#custom-conflict-handler",children:"revisions"}),"."]}),"\n",(0,i.jsx)(s.li,{children:"Store partial merges, track versions, or preserve multiple user edits."}),"\n",(0,i.jsx)(s.li,{children:"Fine-tune how your data merges to ensure consistency across distributed systems."}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"4-reduced-cloud-costs",children:"4. Reduced Cloud Costs"}),"\n",(0,i.jsxs)(s.p,{children:["Firestore queries often count as billable reads. With RxDB, queries run ",(0,i.jsx)(s.strong,{children:"locally"})," against your local state - no repeated network calls or extra charges. You pay only for the data actually synced, not every read. For ",(0,i.jsx)(s.strong,{children:"read-heavy"})," apps, using RxDB as a Firestore alternative can significantly reduce costs."]}),"\n",(0,i.jsx)(s.h3,{id:"5-no-limits-on-query-features",children:"5. No Limits on Query Features"}),"\n",(0,i.jsx)(s.p,{children:"Firestore's query engine is limited by certain constraints (e.g., no advanced joins, limited indexing). With RxDB:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"NoSQL"})," data is stored locally, and you can define any indexes you need."]}),"\n",(0,i.jsxs)(s.li,{children:["Perform ",(0,i.jsx)(s.a,{href:"/rx-query.html",children:"complex queries"}),", run ",(0,i.jsx)(s.a,{href:"/fulltext-search.html",children:"full-text search"}),", or do aggregated transformations or even ",(0,i.jsx)(s.a,{href:"/articles/javascript-vector-database.html",children:"vector search"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["Use ",(0,i.jsx)(s.a,{href:"/rx-query.html#observe",children:"RxDB's reactivity"})," to subscribe to query results in real time."]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"6-true-offline-start-support",children:"6. True Offline-Start Support"}),"\n",(0,i.jsxs)(s.p,{children:["While Firestore does have offline caching, it often requires an online check at app initialization for authentication. RxDB is ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"truly offline-first"}),"; you can launch the app and write data even if the device never goes online initially. It's ready whenever the user is."]}),"\n",(0,i.jsx)(s.h3,{id:"7-cross-platform-any-javascript-runtime",children:"7. Cross-Platform: Any JavaScript Runtime"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB is designed to run in ",(0,i.jsx)(s.strong,{children:"any environment"})," that can execute JavaScript. Whether you\u2019re building a web app in the browser, an ",(0,i.jsx)(s.a,{href:"/electron-database.html",children:"Electron"})," desktop application, a ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"})," mobile app, or a command-line tool with ",(0,i.jsx)(s.a,{href:"/nodejs-database.html",children:"Node.js"}),", RxDB\u2019s storage layer is swappable to fit your runtime\u2019s capabilities."]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["In the ",(0,i.jsx)(s.strong,{children:"browser"}),", store data in ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," or ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["In ",(0,i.jsx)(s.a,{href:"/nodejs-database.html",children:"Node.js"}),", use LevelDB or other supported storages."]}),"\n",(0,i.jsxs)(s.li,{children:["In ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"}),", pick from a range of adapters suited for mobile devices."]}),"\n",(0,i.jsxs)(s.li,{children:["In ",(0,i.jsx)(s.a,{href:"/electron-database.html",children:"Electron"}),", rely on fast local storage with zero changes to your application code."]}),"\n"]}),"\n",(0,i.jsx)(s.hr,{}),"\n",(0,i.jsx)(s.h2,{id:"how-does-rxdbs-sync-work",children:"How Does RxDB's Sync Work?"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB replication is powered by its own ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Sync Engine"}),". This simple yet robust protocol enables:"]}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Pull"}),": Fetch new or updated documents from the server."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Push"}),": Send local changes back to the server."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Live Real-Time"}),": Once you're caught up, you can opt for event-based streaming instead of continuous polling."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"Code Example: Sync RxDB with a Custom Backend"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateRxCollection } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" eventReduce"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'task schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'boolean'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Start a custom REST-based replication"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-tasks-rest-api'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (documents) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Send docs to your REST endpoint"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'https://myapi.com/push'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" method"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'POST'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" body"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ docs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" documents })"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Return conflicts if any"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (lastCheckpoint"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Fetch from your REST endpoint"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`https://myapi.com/pull?checkpoint="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(lastCheckpoint)"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"&limit="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // keep watching for changes"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["By swapping out the handler implementations or using an official plugin (e.g., ",(0,i.jsx)(s.a,{href:"/replication-graphql.html",children:"GraphQL"}),", ",(0,i.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB"}),", ",(0,i.jsx)(s.a,{href:"/replication-firestore.html",children:"Firestore replication"}),", etc.), you can adapt to any backend or data source. RxDB thus becomes a flexible alternative to Firestore while maintaining ",(0,i.jsx)(s.a,{href:"/articles/realtime-database.html",children:"real-time capabilities"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"getting-started-with-rxdb-as-a-firestore-alternative",children:"Getting Started with RxDB as a Firestore Alternative"}),"\n",(0,i.jsx)(s.h3,{id:"install-rxdb",children:"Install RxDB:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"})]})})})}),"\n",(0,i.jsx)(s.h3,{id:"create-a-database",children:"Create a Database:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"define-collections",children:"Define Collections:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" items"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'items schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"sync",children:"Sync"}),"\n",(0,i.jsxs)(s.p,{children:["Use a ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Replication Plugin"})," to connect with a custom backend or existing database."]}),"\n",(0,i.jsxs)(s.p,{children:["For a Firestore-specific approach, RxDB ",(0,i.jsx)(s.a,{href:"/replication-firestore.html",children:"Firestore Replication"})," also exists if you want to combine local indexing and advanced queries with a Cloud Firestore backend. But if you really want to replace Firestore entirely - just point RxDB to your new backend."]}),"\n",(0,i.jsx)(s.h3,{id:"example-start-a-webrtc-p2p-replication",children:"Example: Start a WebRTC P2P Replication"}),"\n",(0,i.jsxs)(s.p,{children:["In addition to syncing with a central server, RxDB also supports pure peer-to-peer replication using ",(0,i.jsx)(s.a,{href:"/replication-webrtc.html",children:"WebRTC"}),". This can be invaluable for scenarios where clients need to sync data directly without a master server."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicateWebRTC"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getConnectionHandlerSimplePeer"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-webrtc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationPool"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateWebRTC"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" topic"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-p2p-room'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Clients with the same topic will sync with each other."})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" connectionHandlerCreator"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getConnectionHandlerSimplePeer"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Use your own or the official RxDB signaling server"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" signalingServerUrl"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'wss://signaling.rxdb.info/'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Node.js requires a polyfill for WebRTC & WebSocket"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" wrtc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'node-datachannel/polyfill'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" webSocketConstructor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'ws'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:").WebSocket"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional pull config"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// optional push config"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// The replicationPool manages all connected peers"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationPool"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"error$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(err "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".error"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'P2P Sync Error:'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" err);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["This example sets up a live ",(0,i.jsx)(s.strong,{children:"P2P replication"})," where any new peers joining the same topic automatically sync local data with each other, eliminating the need for a dedicated central server for the actual data exchange."]}),"\n",(0,i.jsx)(s.h2,{id:"is-rxdb-right-for-your-project",children:"Is RxDB Right for Your Project?"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"You want offline-first"}),": If you need an offline-first app that starts offline, RxDB's local database approach and sync protocol excel at this."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Your project is read-heavy"}),": Reading from Firestore for every query can get expensive. With RxDB, reads are free and local; you only pay for writes or sync overhead."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"You need advanced queries"}),": Firestore's query constraints may not suit complex data. With RxDB, you can define your own indexing logic or run arbitrary queries locally."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"You want no vendor lock-in"}),": Easily transition from Firestore to your own server or another vendor - just change the replication layer."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsx)(s.p,{children:"If you've been searching for a Firestore alternative that gives you the freedom to sync your data with any backend, offers robust offline-first capabilities, and supports truly customizable conflict resolution and queries, RxDB is worth exploring. You can adopt it seamlessly, ensure local reads, reduce costs, and stay in complete control of your data layer."}),"\n",(0,i.jsx)(s.p,{children:"Ready to dive in? Check out the RxDB Quickstart Guide, join our Discord community, and experience how RxDB can be the perfect local-first, real-time database solution for your next project."}),"\n",(0,i.jsx)(s.p,{children:"More resources:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"/replication.html",children:"RxDB Sync Engine"})}),"\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"/replication-firestore.html",children:"Firestore Replication Plugin"})}),"\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"Custom Conflict Resolution"})}),"\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"/code/",children:"RxDB GitHub Repository"})}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>l});var n=r(6540);const i={},o=n.createContext(i);function a(e){const s=n.useContext(o);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),n.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/34f94d1b.29ea6eee.js b/docs/assets/js/34f94d1b.29ea6eee.js
deleted file mode 100644
index 217a8530ba1..00000000000
--- a/docs/assets/js/34f94d1b.29ea6eee.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5832],{8274(e,s,r){r.r(s),r.d(s,{assets:()=>l,contentTitle:()=>i,default:()=>h,frontMatter:()=>a,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"electron-database","title":"Electron Database - Storage adapters for SQLite, Filesystem and In-Memory","description":"Harness the database power of SQLite, Filesystem, and in-memory storage in Electron with RxDB. Build fast, offline-first apps that sync in real time.","source":"@site/docs/electron-database.md","sourceDirName":".","slug":"/electron-database.html","permalink":"/electron-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Electron Database - Storage adapters for SQLite, Filesystem and In-Memory","slug":"electron-database.html","description":"Harness the database power of SQLite, Filesystem, and in-memory storage in Electron with RxDB. Build fast, offline-first apps that sync in real time."},"sidebar":"tutorialSidebar","previous":{"title":"Alternatives for realtime local-first JavaScript applications and local databases","permalink":"/alternatives.html"},"next":{"title":"Building an Optimistic UI with RxDB","permalink":"/articles/optimistic-ui.html"}}');var o=r(4848),t=r(8453);const a={title:"Electron Database - Storage adapters for SQLite, Filesystem and In-Memory",slug:"electron-database.html",description:"Harness the database power of SQLite, Filesystem, and in-memory storage in Electron with RxDB. Build fast, offline-first apps that sync in real time."},i="Electron Database - RxDB with different storage for SQLite, Filesystem and In-Memory",l={},c=[{value:"Databases for Electron",id:"databases-for-electron",level:2},{value:"Server Side Databases in Electron.js",id:"server-side-databases-in-electronjs",level:3},{value:"Localstorage / IndexedDB / WebSQL as alternatives to SQLite in Electron",id:"localstorage--indexeddb--websql-as-alternatives-to-sqlite-in-electron",level:3},{value:"RxDB",id:"rxdb",level:3},{value:"SQLite in Electron.js without RxDB",id:"sqlite-in-electronjs-without-rxdb",level:3},{value:"Follow up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"electron-database---rxdb-with-different-storage-for-sqlite-filesystem-and-in-memory",children:"Electron Database - RxDB with different storage for SQLite, Filesystem and In-Memory"})}),"\n",(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.a,{href:"https://www.electronjs.org/",children:"Electron"})," (aka Electron.js) is a framework developed by github that is designed to create desktop applications with the Web technology stack consisting of HTML, CSS and JavaScript.\nBecause the desktop application runs on the client's device, it is suitable to use a database that can store and query data locally. This allows you to create so-called ",(0,o.jsx)(s.a,{href:"/offline-first.html",children:"local first"})," apps that store data locally and even work when the user has no internet connection.\nWhile there are many options to store data in Electron, for complex realtime apps using ",(0,o.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," is recommended because it is a database made for UI-based client-side application, not a server-side database."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/icons/electron.svg",alt:"Electron",width:"70"})}),"\n",(0,o.jsx)(s.h2,{id:"databases-for-electron",children:"Databases for Electron"}),"\n",(0,o.jsx)(s.p,{children:"An Electron runtime can be divided into two parts:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsx)(s.li,{children:'The "main" process which is a Node.js JavaScript process that runs without a UI in the background.'}),"\n",(0,o.jsx)(s.li,{children:'One or multiple "renderer" processes that consist of a Chrome browser engine and runs the user interface. Each renderer process represents one "browser tab".'}),"\n"]}),"\n",(0,o.jsx)(s.p,{children:"This is important to understand because choosing the right database depends on your use case and on which of these JavaScript runtimes you want to keep the data."}),"\n",(0,o.jsx)(s.h3,{id:"server-side-databases-in-electronjs",children:"Server Side Databases in Electron.js"}),"\n",(0,o.jsxs)(s.p,{children:['Because Electron runs on a desktop computer, you might think that it should be possible to use a common "server" database like MySQL, PostgreSQL or MongoDB. In theory, you could ship the correct database server binaries with your electron application and start a process on the client\'s device that exposes a port to the database that can be consumed by Electron. In practice, this is not a viable way to go because shipping the correct binaries and opening ports is way to complicated and troublesome. Instead you should use a database that can be bundled and run ',(0,o.jsx)(s.strong,{children:"inside"})," of Electron, either in the ",(0,o.jsx)(s.em,{children:"main"})," or in the ",(0,o.jsx)(s.em,{children:"renderer"})," process."]}),"\n",(0,o.jsx)(s.h3,{id:"localstorage--indexeddb--websql-as-alternatives-to-sqlite-in-electron",children:"Localstorage / IndexedDB / WebSQL as alternatives to SQLite in Electron"}),"\n",(0,o.jsxs)(s.p,{children:["Because Electron uses a common Chrome web browser in the renderer process, you can access the common Web Storage APIs like ",(0,o.jsx)(s.a,{href:"/articles/localstorage.html",children:"Localstorage"}),", IndexedDB and WebSQL. This is easy to set up and storing small sets of data can be achieved in a short span of time."]}),"\n",(0,o.jsxs)(s.p,{children:["But as soon as your application goes beyond a simple todo-app, there are multiple obstacles that come in your way. One thing is the bad multi-tab support. If you have more than one ",(0,o.jsx)(s.em,{children:"renderer"})," process, it becomes hard to manage database writes between them. Each ",(0,o.jsx)(s.em,{children:"browser tab"})," could modify the database state while the others do not know of the changes and keep an outdated UI."]}),"\n",(0,o.jsxs)(s.p,{children:["Another thing is performance. ",(0,o.jsx)(s.a,{href:"/slow-indexeddb.html",children:"IndexedDB is slow"}),", mostly because it has to go through layers of browser security and abstractions. Storing and querying a lot of data might become your performance bottleneck. Localstorage and WebSQL are even slower, by the way. Using these Web Storage APIs is generally only recommended when you know for sure that there will be always only ",(0,o.jsx)(s.strong,{children:"one rendering process"}),' and performance is not that relevant. The main reason for that is the security- and abstraction layers that write- and read operations have to go through when using the browsers IndexedDB API. So instead of using IndexedDB in Electron in the renderer process, you should use something that runs in the "main" process in Node.js like the ',(0,o.jsx)(s.a,{href:"/rx-storage-filesystem-node.html",children:"Filesystem RxStorage"})," or the ",(0,o.jsx)(s.a,{href:"/rx-storage-memory.html",children:"In Memory RxStorage"}),"."]}),"\n",(0,o.jsx)(s.h3,{id:"rxdb",children:"RxDB"}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/logo/rxdb_javascript_database.svg",alt:"RxDB",width:"170"})}),"\n",(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," is a NoSQL database for JavaScript applications. It has many features that come in handy when RxDB is used with UI based applications like your Electron app. For example, it is able to subscribe to query results of single fields of documents. It has encryption and compression features and most important it has a battle tested ",(0,o.jsx)(s.a,{href:"/replication.html",children:"Sync Engine"})," that can be used to do a realtime sync with your backend."]}),"\n",(0,o.jsxs)(s.p,{children:["Because of the ",(0,o.jsx)(s.a,{href:"https://rxdb.info/rx-storage.html",children:"flexible storage"})," layer of RxDB, there are many options on how to use it with Electron:"]}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:["The ",(0,o.jsx)(s.a,{href:"/rx-storage-memory.html",children:"memory RxStorage"})," that stores the data inside of the JavaScript memory without persistence"]}),"\n",(0,o.jsxs)(s.li,{children:["The ",(0,o.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})]}),"\n",(0,o.jsxs)(s.li,{children:["The ",(0,o.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"})]}),"\n",(0,o.jsxs)(s.li,{children:["The ",(0,o.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"})]}),"\n",(0,o.jsxs)(s.li,{children:["The ",(0,o.jsx)(s.a,{href:"/rx-storage-dexie.html",children:"Dexie.js RxStorage"})]}),"\n",(0,o.jsxs)(s.li,{children:["The ",(0,o.jsx)(s.a,{href:"/rx-storage-filesystem-node.html",children:"Node.js Filesystem"})]}),"\n"]}),"\n",(0,o.jsxs)(s.p,{children:["It is recommended to use the ",(0,o.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," because it has the best performance and is the easiest to set up. However it is part of the ",(0,o.jsx)(s.a,{href:"/premium/",children:"\ud83d\udc51 Premium Plugins"})," which must be purchased, so to try out RxDB with Electron, you might want to use one of the other options. To start with RxDB, I would recommend using the LocalStorage RxStorage in the renderer processes. Because RxDB is able to broadcast the database state between browser tabs, having multiple renderer processes is not a problem like it would be when you use plain IndexedDB without RxDB.\nIn production, you would always run the RxStorage in the main process with the ",(0,o.jsx)(s.a,{href:"/electron.html#rxstorage-electron-ipcrenderer--ipcmain",children:"RxStorage Electron IpcRenderer & IpcMain"})," plugins."]}),"\n",(0,o.jsxs)(s.p,{children:["First, you have to install all dependencies via ",(0,o.jsx)(s.code,{children:"npm install rxdb rxjs"}),".\nThen you can assemble the RxStorage and create a database with it:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create database"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create collections"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert document"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"humans"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// run a query"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"humans"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// observe a query"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"humans"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(result "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]})]})})}),"\n",(0,o.jsxs)(s.p,{children:["For better performance in the renderer tab, you can later switch to the ",(0,o.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),". But in production, it is recommended to use the ",(0,o.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," or the ",(0,o.jsx)(s.a,{href:"/rx-storage-filesystem-node.html",children:"Filesystem RxStorage"})," in the main process so that database operations do not block the rendering of the UI.\nTo learn more about using RxDB with Electron, you might want to check out ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/electron",children:"this example project"}),"."]}),"\n",(0,o.jsx)(s.h3,{id:"sqlite-in-electronjs-without-rxdb",children:"SQLite in Electron.js without RxDB"}),"\n",(0,o.jsx)(s.p,{children:"SQLite is a SQL based relational database written in the C programming language that was crafted to be embedded inside of applications and stores data locally. Operations are written in the SQL query language similar to the PostgreSQL syntax."}),"\n",(0,o.jsxs)(s.p,{children:["Using SQLite in Electron is not possible in the ",(0,o.jsx)(s.em,{children:"renderer process"}),", only in the ",(0,o.jsx)(s.em,{children:"main process"}),". To communicate data operations between your main and your renderer processes, you have to use either ",(0,o.jsx)(s.a,{href:"https://github.com/electron/remote",children:"@electron/remote"})," (not recommended) or the ",(0,o.jsx)(s.a,{href:"https://www.electronjs.org/de/docs/latest/api/ipc-renderer",children:"ipcRenderer"})," (recommended). So you start up SQLite in your main process and whenever you want to read or write data, you send the SQL queries to the main process and retrieve the result back as JSON data."]}),"\n",(0,o.jsxs)(s.p,{children:["To install SQLite, use the ",(0,o.jsx)(s.a,{href:"https://github.com/TryGhost/node-sqlite3",children:"SQLite3"})," package which is a native Node.js module. You also need the ",(0,o.jsx)(s.a,{href:"https://github.com/electron/rebuild",children:"@electron/rebuild"})," package to rebuild the SQLite module against the currently installed Electron version."]}),"\n",(0,o.jsxs)(s.p,{children:["Install them with ",(0,o.jsx)(s.code,{children:"npm install sqlite3 @electron/rebuild"}),".\nThen you can rebuild SQLite with ",(0,o.jsx)(s.code,{children:"./node_modules/.bin/electron-rebuild -f -w sqlite3"}),"\nIn the JavaScript code of your main process you can now create a database:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sqlite3"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'sqlite3'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sqlite3"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".Database"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'/path/to/database/file.db'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create a table and insert a row"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".serialize"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".run"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"CREATE TABLE Users (name, lastName)"'}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".run"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"INSERT INTO Users VALUES (?, ?)"'}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foo'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]);"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.p,{children:"Also you have to set up the ipcRenderer so that message from the renderer process are handled:"}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"ipcMain"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".handle"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'db-query'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (event"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqlQuery) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".all"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlQuery"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (err"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" rows) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" res"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(rows);"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.p,{children:"In your renderer process, you can now call the ipcHandler and fetch data from SQLite:"}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsx)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" rows"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" ipcRenderer"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".invoke"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'db-query'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "SELECT * FROM Users"'}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})})})}),"\n",(0,o.jsxs)(s.p,{children:["The downside of SQLite (or SQL in general) is that it is lacking many features that are handful when using a database together with ",(0,o.jsx)(s.strong,{children:"UI based"})," applications. It is not possible to observe queries or document fields and there is no replication method to sync data with a server. This makes SQLite a good solution when you just want to store data on the client or process expensive SQL queries on the server, but it is not suitable for more complex operations like two-way replication, encryption, compression and so on. Also developer helpers like TypeScript type safety are totally out of reach."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/logo/rxdb_javascript_database.svg",alt:"RxDB Electron Database",width:"170"})}),"\n",(0,o.jsx)(s.h2,{id:"follow-up",children:"Follow up"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:["Learn how to use RxDB as database in electron with the ",(0,o.jsx)(s.a,{href:"/quickstart.html",children:"Quickstart Tutorial"}),"."]}),"\n",(0,o.jsxs)(s.li,{children:["Check out the ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/electron",children:"RxDB Electron example"})]}),"\n",(0,o.jsxs)(s.li,{children:["There is a followup list of other ",(0,o.jsx)(s.a,{href:"/alternatives.html",children:"client side database alternatives"})," that you can try to use with Electron."]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,t.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>i});var n=r(6540);const o={},t=n.createContext(o);function a(e){const s=n.useContext(t);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function i(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),n.createElement(t.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/35e4d287.95750a0a.js b/docs/assets/js/35e4d287.95750a0a.js
deleted file mode 100644
index 797bb9fb0ad..00000000000
--- a/docs/assets/js/35e4d287.95750a0a.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7853],{383(e,n,i){i.r(n),i.d(n,{assets:()=>d,contentTitle:()=>o,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>a});const r=JSON.parse('{"id":"releases/17.0.0","title":"RxDB 17.0.0","description":"RxDB 17 introduces improved reactivity APIs, better debugging, breaking storage fixes, and multiple plugins graduating from beta.","source":"@site/docs/releases/17.0.0.md","sourceDirName":"releases","slug":"/releases/17.0.0.html","permalink":"/releases/17.0.0.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB 17.0.0","slug":"17.0.0.html","description":"RxDB 17 introduces improved reactivity APIs, better debugging, breaking storage fixes, and multiple plugins graduating from beta."},"sidebar":"tutorialSidebar","previous":{"title":"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite","permalink":"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html"},"next":{"title":"16.0.0","permalink":"/releases/16.0.0.html"}}');var s=i(4848),t=i(8453);const l={title:"RxDB 17.0.0",slug:"17.0.0.html",description:"RxDB 17 introduces improved reactivity APIs, better debugging, breaking storage fixes, and multiple plugins graduating from beta."},o="RxDB 17.0.0 (beta)",d={},a=[{value:"Migration",id:"migration",level:2},{value:"Storage migration required",id:"storage-migration-required",level:3},{value:"Other migration notes",id:"other-migration-notes",level:3},{value:"CHANGES",id:"changes",level:2},{value:"Features",id:"features",level:3},{value:"\ud83d\udd01 Reactivity & APIs",id:"-reactivity--apis",level:3},{value:"\ud83e\udde0 Debugging & Developer Experience",id:"-debugging--developer-experience",level:3},{value:"\ud83d\uddc4\ufe0f Storage & Replication Fixes",id:"\ufe0f-storage--replication-fixes",level:3},{value:"\ud83d\udcd0 Schema & Index Validation",id:"-schema--index-validation",level:3},{value:"\u2699\ufe0f Performance & Internal Changes",id:"\ufe0f-performance--internal-changes",level:3},{value:"\ud83d\udd0c Plugins Graduating from Beta",id:"-plugins-graduating-from-beta",level:3},{value:"You can help!",id:"you-can-help",level:2},{value:"LinkedIn",id:"linkedin",level:2}];function c(e){const n={a:"a",admonition:"admonition",br:"br",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"rxdb-1700-beta",children:"RxDB 17.0.0 (beta)"})}),"\n",(0,s.jsxs)(n.p,{children:["RxDB 17 focuses on ",(0,s.jsx)(n.strong,{children:"better reactivity"}),", ",(0,s.jsx)(n.strong,{children:"improved debugging"}),", and ",(0,s.jsx)(n.strong,{children:"important storage fixes"}),", while also graduating several long-standing plugins out of beta."]}),"\n",(0,s.jsxs)(n.p,{children:["Most applications can upgrade easily, but ",(0,s.jsx)(n.strong,{children:"users of OPFS and filesystem-based storages must review the migration notes carefully"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["RxDB version 17 is currently in ",(0,s.jsx)(n.strong,{children:"beta"}),". For testing, please install the latest beta version ",(0,s.jsx)(n.a,{href:"https://www.npmjs.com/package/rxdb?activeTab=versions",children:"from npm"})," and provide feedback or report issues ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/7574",children:"on GitHub"}),"."]})}),"\n",(0,s.jsx)(n.h2,{id:"migration",children:"Migration"}),"\n",(0,s.jsx)(n.p,{children:"RxDB 17 is mostly backward-compatible, but please review the following points before upgrading:"}),"\n",(0,s.jsx)(n.h3,{id:"storage-migration-required",children:"Storage migration required"}),"\n",(0,s.jsxs)(n.p,{children:["If you use any of the following storages, ",(0,s.jsx)(n.strong,{children:"you must migrate your data"})," using the ",(0,s.jsx)(n.a,{href:"/migration-storage.html",children:"storage migrator"}),":"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"OPFS RxStorage"}),"\n",(0,s.jsx)(n.li,{children:"Filesystem RxStorage (Node)"}),"\n",(0,s.jsx)(n.li,{children:"IndexedDB RxStorage (if you use attachments)"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"For all other RxStorages, data created with RxDB v16 can be reused directly in RxDB v17."}),"\n",(0,s.jsx)(n.h3,{id:"other-migration-notes",children:"Other migration notes"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["The GitHub repository ",(0,s.jsxs)(n.strong,{children:["no longer contains prebuilt ",(0,s.jsx)(n.code,{children:"dist"})," files"]}),".",(0,s.jsx)(n.br,{}),"\n","Install RxDB from npm or run the build scripts locally."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"toggleOnDocumentVisible"})," now defaults to ",(0,s.jsx)(n.strong,{children:(0,s.jsx)(n.code,{children:"true"})}),". ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/6810",children:"#6810"})]}),"\n",(0,s.jsxs)(n.li,{children:["Schema fields marked as ",(0,s.jsx)(n.code,{children:"final"})," ",(0,s.jsxs)(n.strong,{children:["no longer need to be explicitly listed as ",(0,s.jsx)(n.code,{children:"required"})]}),"."]}),"\n",(0,s.jsxs)(n.li,{children:["Some integrations now use ",(0,s.jsx)(n.strong,{children:"optional peer dependencies"}),". You may need to install them manually:","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"firebase"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"mongodb"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"nats"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"changes",children:"CHANGES"}),"\n",(0,s.jsx)(n.h3,{id:"features",children:"Features"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Added ",(0,s.jsx)(n.a,{href:"/react.html",children:"react-hooks plugin"}),"."]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"-reactivity--apis",children:"\ud83d\udd01 Reactivity & APIs"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," ",(0,s.jsx)(n.code,{children:"RxDatabase.collections$"})," observable for reactive access to collections"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," support for the JavaScript ",(0,s.jsx)(n.code,{children:"using"})," keyword to automatically remove databases in tests"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," new ",(0,s.jsx)(n.code,{children:"reactivity-angular"})," package"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"CHANGE"})," moved ",(0,s.jsx)(n.code,{children:"reactivity-vue"})," and ",(0,s.jsx)(n.code,{children:"reactivity-preact-signals"})," from premium to core"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"FIX"})," query results becoming incorrect when changes occur faster than query update ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/7067",children:"#7067"})]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"-debugging--developer-experience",children:"\ud83e\udde0 Debugging & Developer Experience"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," ",(0,s.jsx)(n.code,{children:"context"})," field to all RxDB write errors for easier debugging"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," improved OPFS RxStorage error logging in ",(0,s.jsx)(n.code,{children:"devMode"}),", including:","\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["strict ",(0,s.jsx)(n.code,{children:"TextDecoder"})," mode"]}),"\n",(0,s.jsx)(n.li,{children:"detailed decoding error output"}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," internal ",(0,s.jsx)(n.code,{children:"WeakRef"})," TypeScript types so users no longer need to enable ",(0,s.jsx)(n.code,{children:"ES2021.WeakRef"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," ",(0,s.jsx)(n.a,{href:"https://rxdb.info/llms.txt",children:"llms.txt"})," for LLM-friendly documentation access"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"CHANGE"})," ",(0,s.jsx)(n.code,{children:"toggleOnDocumentVisible"})," now defaults to ",(0,s.jsx)(n.code,{children:"true"})," ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/6810",children:"#6810"})]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"\ufe0f-storage--replication-fixes",children:"\ud83d\uddc4\ufe0f Storage & Replication Fixes"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"FIX"})," OPFS RxStorage memory and cleanup leaks"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"FIX"})," memory-mapped storage not purging deleted documents"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"FIX"})," ",(0,s.jsx)(n.code,{children:"RxCollection.cleanup()"})," ignoring ",(0,s.jsx)(n.code,{children:"minimumDeletedTime"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"FIX"})," short primary key lengths not matching replication schema ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/7587",children:"#7587"})]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"FIX"})," retry DenoKV commits when encountering ",(0,s.jsx)(n.code,{children:'"database is locked"'})," errors"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"FIX"})," close broadcast channels and leader election ",(0,s.jsx)(n.strong,{children:"after"})," database shutdown, not during"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"CHANGE"}),": Store data as binary in IndexedDB to use less disc space."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"CHANGE"}),": Pull-Only replications no longer store the server metadata on the client."]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"-schema--index-validation",children:"\ud83d\udcd0 Schema & Index Validation"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," enforce maximum length for indexes and primary keys (",(0,s.jsx)(n.code,{children:"maxLength: 2048"}),")"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"CHANGE"})," ",(0,s.jsx)(n.code,{children:"final"})," schema fields no longer need to be marked as ",(0,s.jsx)(n.code,{children:"required"})]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"\ufe0f-performance--internal-changes",children:"\u2699\ufe0f Performance & Internal Changes"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"CHANGE"})," replace ",(0,s.jsx)(n.code,{children:"appendToArray()"})," with ",(0,s.jsx)(n.code,{children:"Array.concat()"})," for better browser-level optimizations"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"ADD"})," ensure indexes and primary keys are validated consistently across replication schemas"]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"-plugins-graduating-from-beta",children:"\ud83d\udd0c Plugins Graduating from Beta"}),"\n",(0,s.jsxs)(n.p,{children:["The following plugins are ",(0,s.jsx)(n.strong,{children:"no longer in beta"})," and are now considered production-ready:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Replication Appwrite"}),"\n",(0,s.jsx)(n.li,{children:"Replication Supabase"}),"\n",(0,s.jsx)(n.li,{children:"Replication MongoDB"}),"\n",(0,s.jsx)(n.li,{children:"RxStorage MongoDB"}),"\n",(0,s.jsx)(n.li,{children:"RxStorage Filesystem (Node)"}),"\n",(0,s.jsx)(n.li,{children:"RxStorage DenoKV"}),"\n",(0,s.jsx)(n.li,{children:"Attachment replication"}),"\n",(0,s.jsx)(n.li,{children:"CRDT Plugin"}),"\n",(0,s.jsx)(n.li,{children:"RxPipeline"}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"you-can-help",children:"You can help!"}),"\n",(0,s.jsxs)(n.p,{children:["There are many things that can be done by ",(0,s.jsx)(n.strong,{children:"you"})," to improve RxDB:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Check the ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/BACKLOG.md",children:"BACKLOG"})," for features that would be great to have."]}),"\n",(0,s.jsxs)(n.li,{children:["Check the ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/before-next-major.md",children:"breaking backlog"})," for breaking changes that must be implemented in the future but where I did not have the time yet."]}),"\n",(0,s.jsxs)(n.li,{children:["Check the ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/search?q=todo",children:"todos"})," in the code. There are many small improvements that can be done for performance and build size."]}),"\n",(0,s.jsx)(n.li,{children:"Review the code and add tests. I am only a single human with a laptop. My code is not perfect and much small improvements can be done when people review the code and help me to clarify undefined behaviors."}),"\n",(0,s.jsxs)(n.li,{children:["Update the ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"example projects"})," some of them are outdated and need updates."]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"linkedin",children:"LinkedIn"}),"\n",(0,s.jsxs)(n.p,{children:["Stay connected with the latest updates and network with professionals in the RxDB community by following RxDB's ",(0,s.jsx)(n.a,{href:"https://www.linkedin.com/company/rxdb",children:"official LinkedIn page"}),"!"]})]})}function h(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(c,{...e})}):c(e)}},8453(e,n,i){i.d(n,{R:()=>l,x:()=>o});var r=i(6540);const s={},t=r.createContext(s);function l(e){const n=r.useContext(t);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:l(e.components),r.createElement(t.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/36715375.d4b64326.js b/docs/assets/js/36715375.d4b64326.js
deleted file mode 100644
index 213ced1e5be..00000000000
--- a/docs/assets/js/36715375.d4b64326.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2076],{2323(e,t,i){i.r(t),i.d(t,{default:()=>o});var s=i(4586),r=i(8711),c=i(6540),l=i(8141),d=i(4848);function o(){const{siteConfig:e}=(0,s.A)();return(0,c.useEffect)(()=>{(0,l.c)("goto_code",.4)}),(0,d.jsx)(r.A,{title:`Code - ${e.title}`,description:"RxDB Source Code",children:(0,d.jsx)("main",{children:(0,d.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,d.jsx)("a",{href:"/",children:(0,d.jsx)("div",{className:"logo",children:(0,d.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,d.jsx)("h1",{children:"RxDB Code"}),(0,d.jsx)("p",{children:(0,d.jsx)("b",{children:"You will be redirected in a few seconds."})}),(0,d.jsx)("p",{children:(0,d.jsx)("a",{href:"https://github.com/pubkey/rxdb",children:"Click here to open Code"})}),(0,d.jsx)("meta",{httpEquiv:"Refresh",content:"1; url=https://github.com/pubkey/rxdb"})]})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/37055470.55528f37.js b/docs/assets/js/37055470.55528f37.js
deleted file mode 100644
index dc0f1ed882c..00000000000
--- a/docs/assets/js/37055470.55528f37.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4970],{9528(a,e,t){t.r(e),t.d(e,{default:()=>l});var r=t(544),s=t(4848);function l(){return(0,r.default)({sem:{id:"reddit",metaTitle:"The local Database for Angular Apps",appName:"Angular",title:(0,s.jsxs)(s.Fragment,{children:["The easiest way to ",(0,s.jsx)("b",{children:"store"})," and ",(0,s.jsx)("b",{children:"sync"})," Data"]})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/380cc66a.7da5e510.js b/docs/assets/js/380cc66a.7da5e510.js
deleted file mode 100644
index 69045977287..00000000000
--- a/docs/assets/js/380cc66a.7da5e510.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2061],{12(e,s,r){r.r(s),r.d(s,{assets:()=>d,contentTitle:()=>t,default:()=>x,frontMatter:()=>l,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"rx-storage-dexie","title":"RxDB Dexie.js Database - Fast, Reactive, Sync with Any Backend","description":"Use Dexie.js to power RxDB in the browser. Enjoy quick setup, Dexie addons, and reliable storage for small apps or prototypes.","source":"@site/docs/rx-storage-dexie.md","sourceDirName":".","slug":"/rx-storage-dexie.html","permalink":"/rx-storage-dexie.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB Dexie.js Database - Fast, Reactive, Sync with Any Backend","slug":"rx-storage-dexie.html","description":"Use Dexie.js to power RxDB in the browser. Enjoy quick setup, Dexie addons, and reliable storage for small apps or prototypes."},"sidebar":"tutorialSidebar","previous":{"title":"SQLite (Capacitor, React-Native, Expo, Tauri, Electron, Node.js)","permalink":"/rx-storage-sqlite.html"},"next":{"title":"MongoDB","permalink":"/rx-storage-mongodb.html"}}');var i=r(4848),o=r(8453),a=r(3247);const l={title:"RxDB Dexie.js Database - Fast, Reactive, Sync with Any Backend",slug:"rx-storage-dexie.html",description:"Use Dexie.js to power RxDB in the browser. Enjoy quick setup, Dexie addons, and reliable storage for small apps or prototypes."},t="RxStorage Dexie.js",d={},c=[{value:"Dexie.js vs IndexedDB Storage",id:"dexiejs-vs-indexeddb-storage",level:2},{value:"How to use Dexie.js as a Storage for RxDB",id:"how-to-use-dexiejs-as-a-storage-for-rxdb",level:2},{value:"Import the Dexie Storage",id:"import-the-dexie-storage",level:3},{value:"Create a Database",id:"create-a-database",level:3},{value:"Overwrite/Polyfill the native IndexedDB API with an in-memory version",id:"overwritepolyfill-the-native-indexeddb-api-with-an-in-memory-version",level:2},{value:"Using Dexie Addons",id:"using-dexie-addons",level:2},{value:"Sync Dexie.js with your Backend in RxDB",id:"sync-dexiejs-with-your-backend-in-rxdb",level:2},{value:"A. Use Dexie Cloud Sync",id:"a-use-dexie-cloud-sync",level:3},{value:"Install the Dexie Cloud Addon",id:"install-the-dexie-cloud-addon",level:4},{value:"Import RxDB and dexie-cloud",id:"import-rxdb-and-dexie-cloud",level:4},{value:"Create a Dexie based RxStorage with the Cloud Plugin",id:"create-a-dexie-based-rxstorage-with-the-cloud-plugin",level:4},{value:"Create an RxDB Database",id:"create-an-rxdb-database",level:4},{value:"B. Use the RxDB Replication",id:"b-use-the-rxdb-replication",level:3},{value:"Import the RxDB with dexie and the CouchDB plugin",id:"import-the-rxdb-with-dexie-and-the-couchdb-plugin",level:4},{value:"Create an RxDB Database with the Dexie Storage",id:"create-an-rxdb-database-with-the-dexie-storage",level:4},{value:"Add a Collection",id:"add-a-collection",level:4},{value:"Sync the Collection with a CouchDB Server",id:"sync-the-collection-with-a-couchdb-server",level:4},{value:"liveQuery - Realtime Queries",id:"livequery---realtime-queries",level:2},{value:"Disabling the non-premium console log",id:"disabling-the-non-premium-console-log",level:2},{value:"Performance comparison with other RxStorage plugins",id:"performance-comparison-with-other-rxstorage-plugins",level:2}];function h(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"rxstorage-dexiejs",children:"RxStorage Dexie.js"})}),"\n",(0,i.jsxs)(s.p,{children:["To store the data inside of and RxDB Database in IndexedDB in the ",(0,i.jsx)(s.a,{href:"/articles/browser-database.html",children:"browser"}),", you can use the ",(0,i.jsx)(s.a,{href:"https://github.com/dexie/Dexie.js",children:"Dexie.js"})," based ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"}),". Dexie.js is a minimal wrapper around IndexedDB and the Dexie.js RxStorage wraps that again to use it for an RxDB database in the browser. For side projects and prototypes that run in a browser, you should use the dexie RxStorage as a default."]}),"\n",(0,i.jsx)(s.h2,{id:"dexiejs-vs-indexeddb-storage",children:"Dexie.js vs IndexedDB Storage"}),"\n",(0,i.jsxs)(s.p,{children:["While Dexie.js ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," can be used for free, most professional projects should switch to our ",(0,i.jsxs)(s.strong,{children:["premium ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"})," \ud83d\udc51"]})," in production:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["It is faster and reduces build size by up to ",(0,i.jsx)(s.strong,{children:"36%"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["It has a way ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"better performance"})," on reads and writes."]}),"\n",(0,i.jsx)(s.li,{children:"It stores attachments data as binary instead of base64 which reduces used space by 33%."}),"\n",(0,i.jsxs)(s.li,{children:["It does not use a ",(0,i.jsx)(s.a,{href:"/slow-indexeddb.html#batched-cursor",children:"Batched Cursor"})," or ",(0,i.jsx)(s.a,{href:"/slow-indexeddb.html#custom-indexes",children:"custom indexes"})," which makes queries slower compared to the ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["It supports ",(0,i.jsx)(s.strong,{children:"non-required indexes"})," which is ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/pull/6643#issuecomment-2505310082",children:"not possible"})," with Dexie.js."]}),"\n",(0,i.jsxs)(s.li,{children:["It runs in a ",(0,i.jsx)(s.strong,{children:"WAL-like mode"})," (similar to SQLite) for faster writes and improved responsiveness."]}),"\n",(0,i.jsxs)(s.li,{children:["It support the ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html#storage-buckets",children:"Storage Buckets API"})]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"how-to-use-dexiejs-as-a-storage-for-rxdb",children:"How to use Dexie.js as a Storage for RxDB"}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(s.h3,{id:"import-the-dexie-storage",children:"Import the Dexie Storage"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageDexie } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-dexie'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h3,{id:"create-a-database",children:"Create a Database"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]}),"\n",(0,i.jsx)(s.h2,{id:"overwritepolyfill-the-native-indexeddb-api-with-an-in-memory-version",children:"Overwrite/Polyfill the native IndexedDB API with an in-memory version"}),"\n",(0,i.jsxs)(s.p,{children:["Node.js has no IndexedDB API. To still run the Dexie ",(0,i.jsx)(s.code,{children:"RxStorage"})," in Node.js, for example to run unit tests, you have to polyfill it.\nYou can do that by using the ",(0,i.jsx)(s.a,{href:"https://github.com/dumbmatter/fakeIndexedDB",children:"fake-indexeddb"})," module and pass it to the ",(0,i.jsx)(s.code,{children:"getRxStorageDexie()"})," function."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageDexie } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-dexie'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> npm install fake-indexeddb --save"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" fakeIndexedDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'fake-indexeddb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" fakeIDBKeyRange"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'fake-indexeddb/lib/FDBKeyRange'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexedDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" fakeIndexedDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" IDBKeyRange"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" fakeIDBKeyRange"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsx)(s.h2,{id:"using-dexie-addons",children:"Using Dexie Addons"}),"\n",(0,i.jsxs)(s.p,{children:["Dexie.js has its own plugin system with ",(0,i.jsx)(s.a,{href:"https://dexie.org/docs/DerivedWork#known-addons",children:"many plugins"})," for encryption, replication or other use cases. With the Dexie.js ",(0,i.jsx)(s.code,{children:"RxStorage"})," you can use the same plugins by passing them to the ",(0,i.jsx)(s.code,{children:"getRxStorageDexie()"})," function."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" addons"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [ "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* Your Dexie.js plugins */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"sync-dexiejs-with-your-backend-in-rxdb",children:"Sync Dexie.js with your Backend in RxDB"}),"\n",(0,i.jsx)(s.p,{children:"Having your local data in sync with a remote backend is a key feature of RxDB. Here are two approaches to achieve this when using the Dexie.js RxStorage:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Dexie Cloud"})," provides a ",(0,i.jsx)(s.strong,{children:"managed solution"}),": For quick setups, letting you rely on its Cloud backend and conflict resolution."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/replication.html",children:"RxDB's replication"}),": Offers ",(0,i.jsx)(s.strong,{children:"full control"})," over your backend, data flow, and ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"conflict handling"}),"."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"Choose the approach that best suits your needs - whether you want to get started quickly with Dexie Cloud or require the adaptability and autonomy of RxDB's native replication."}),"\n",(0,i.jsx)(s.h3,{id:"a-use-dexie-cloud-sync",children:"A. Use Dexie Cloud Sync"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Dexie Cloud"})," is an official SaaS solution provided by the Dexie team. It offers automatic synchronization, user management, and conflict resolution out of the box. The primary benefits are:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Automatic Sync"}),": Dexie Cloud keeps your local IndexedDB in sync with its cloud-based backend."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"User Authentication"}),": Built-in user management (auth, roles, permissions)."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Conflict Resolution"}),": Automated resolution logic on the server side."]}),"\n"]}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(s.h4,{id:"install-the-dexie-cloud-addon",children:"Install the Dexie Cloud Addon"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" dexie-cloud-addon"})]})})})}),(0,i.jsx)(s.h4,{id:"import-rxdb-and-dexie-cloud",children:"Import RxDB and dexie-cloud"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageDexie } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-dexie'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" dexieCloud "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'dexie-cloud-addon'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h4,{id:"create-a-dexie-based-rxstorage-with-the-cloud-plugin",children:"Create a Dexie based RxStorage with the Cloud Plugin"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" addons"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [dexieCloud]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /*"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Whenever a new dexie database instance is created,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * this method will be called."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" onCreate"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(dexieDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" dexieDatabaseName) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" dexieDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"cloud"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".configure"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" databaseUrl"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "https://.dexie.cloud"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" requireAuth"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"create-an-rxdb-database",children:"Create an RxDB Database"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]}),"\n",(0,i.jsx)(s.h3,{id:"b-use-the-rxdb-replication",children:"B. Use the RxDB Replication"}),"\n",(0,i.jsxs)(s.p,{children:["For ",(0,i.jsx)(s.strong,{children:"full flexibility"})," over your backend or conflict resolution strategy, you can use one of ",(0,i.jsx)(s.strong,{children:"RxDB's many replication plugins"})," like"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB Replication"})," Plugin: Replicate with a CouchDB Server"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/replication-graphql.html",children:"GraphQL Replication"})," Plugin: Sync data with any GraphQL endpoint. Useful when you have a custom schema or you want to utilize GraphQL's powerful query features."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/replication-http.html",children:"Custom Replication with REST APIs"}),": Implement your own replication by building a pull/push handler that communicates with any RESTful backend."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"Below is an example of replicating an RxDB collection with a CouchDB backend using RxDB's CouchDB replication plugin:"}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(s.h4,{id:"import-the-rxdb-with-dexie-and-the-couchdb-plugin",children:"Import the RxDB with dexie and the CouchDB plugin"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateCouchDB } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-couchdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageDexie } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-dexie'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h4,{id:"create-an-rxdb-database-with-the-dexie-storage",children:"Create an RxDB Database with the Dexie Storage"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"add-a-collection",children:"Add a Collection"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"sync-the-collection-with-a-couchdb-server",children:"Sync the Collection with a CouchDB Server"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateCouchDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-couchdb-replication'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // The URL to your CouchDB endpoint"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/db/humans'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]}),"\n",(0,i.jsx)(s.h2,{id:"livequery---realtime-queries",children:"liveQuery - Realtime Queries"}),"\n",(0,i.jsxs)(s.p,{children:["Dexie.js offers a feature called ",(0,i.jsx)(s.code,{children:"liveQuery"})," which automatically updates query results as data changes, allowing you to react to these changes in real-time. However, because RxDB intrinsically provides ",(0,i.jsx)(s.a,{href:"/rx-query.html#observe",children:"reactive queries"}),", you typically do ",(0,i.jsx)(s.strong,{children:"not"})," need to enable live queries through Dexie. Once you have created your database and collections with RxDB, any query you perform can be observed by subscribing to it, for example via ",(0,i.jsx)(s.code,{children:"collection.find().$.subscribe(results => { /*... */ })"}),". This means RxDB takes care of listening for changes and automatically emitting new results - ensuring your UI stays in sync with the underlying data without requiring extra plugins or manual polling."]}),"\n",(0,i.jsx)(s.h2,{id:"disabling-the-non-premium-console-log",children:"Disabling the non-premium console log"}),"\n",(0,i.jsxs)(s.p,{children:["We want to be transparent with our community, and you'll notice a console message when using the free Dexie.js based RxStorage implementation. This message serves to inform you about the availability of faster storage solutions within our ",(0,i.jsx)(s.a,{href:"/premium/",children:"\ud83d\udc51 Premium Plugins"}),". We understand that this might be a minor inconvenience, and we sincerely apologize for that. However, maintaining and improving RxDB requires substantial resources, and our premium users help us ensure its sustainability. If you find value in RxDB and wish to remove this message, we encourage you to explore our premium storage options, which are optimized for professional use and production environments. Thank you for your understanding and support."]}),"\n",(0,i.jsxs)(s.p,{children:["If you already have premium access and want to use the Dexie.js ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," without the log, you can call the ",(0,i.jsx)(s.code,{children:"setPremiumFlag()"})," function to disable the log."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { setPremiumFlag } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/shared'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"setPremiumFlag"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"performance-comparison-with-other-rxstorage-plugins",children:"Performance comparison with other RxStorage plugins"}),"\n",(0,i.jsx)(s.p,{children:"The performance of the Dexie.js RxStorage is good enough for most use cases but other storages can have way better performance metrics:"}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser Dexie.js",width:"700"})})]})}function x(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},3247(e,s,r){r.d(s,{g:()=>i});var n=r(4848);function i(e){const s=[];let r=null;return e.children.forEach(e=>{e.props.id?(r&&s.push(r),r={headline:e,paragraphs:[]}):r&&r.paragraphs.push(e)}),r&&s.push(r),(0,n.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,n.jsxs)("div",{style:o.stepWrapper,children:[(0,n.jsxs)("div",{style:o.stepIndicator,children:[(0,n.jsx)("div",{style:o.stepNumber,children:s+1}),(0,n.jsx)("div",{style:o.verticalLine})]}),(0,n.jsxs)("div",{style:o.stepContent,children:[(0,n.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,n.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>l});var n=r(6540);const i={},o=n.createContext(i);function a(e){const s=n.useContext(o);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),n.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/38a45a95.a7eb0903.js b/docs/assets/js/38a45a95.a7eb0903.js
deleted file mode 100644
index 05a0ca5e5e2..00000000000
--- a/docs/assets/js/38a45a95.a7eb0903.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[405],{4806(e,a,t){t.r(a),t.d(a,{default:()=>n});var s=t(544),l=t(4848);function n(){return(0,s.default)({sem:{id:"gads",title:(0,l.jsxs)(l.Fragment,{children:["The easiest way to ",(0,l.jsx)("b",{children:"store"})," and ",(0,l.jsx)("b",{children:"sync"})," Data in Electron"]}),appName:"Electron",iconUrl:"/files/icons/electron.svg",metaTitle:"Local Electron Database"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/38bbf12a.62b38600.js b/docs/assets/js/38bbf12a.62b38600.js
deleted file mode 100644
index d37ef992bce..00000000000
--- a/docs/assets/js/38bbf12a.62b38600.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2078],{8251(e,a,i){i.r(a),i.d(a,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>r,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"articles/data-base","title":"Empower Web Apps with Reactive RxDB Data-base","description":"Explore RxDB\'s reactive data-base solution for web and mobile. Enable offline-first experiences, real-time syncing, and secure data handling with ease.","source":"@site/docs/articles/data-base.md","sourceDirName":"articles","slug":"/articles/data-base.html","permalink":"/articles/data-base.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Empower Web Apps with Reactive RxDB Data-base","slug":"data-base.html","description":"Explore RxDB\'s reactive data-base solution for web and mobile. Enable offline-first experiences, real-time syncing, and secure data handling with ease."},"sidebar":"tutorialSidebar","previous":{"title":"Browser Storage - RxDB as a Database for Browsers","permalink":"/articles/browser-storage.html"},"next":{"title":"Embedded Database, Real-time Speed - RxDB","permalink":"/articles/embedded-database.html"}}');var t=i(4848),s=i(8453);const r={title:"Empower Web Apps with Reactive RxDB Data-base",slug:"data-base.html",description:"Explore RxDB's reactive data-base solution for web and mobile. Enable offline-first experiences, real-time syncing, and secure data handling with ease."},o="RxDB as a data base: Empowering Web Applications with Reactive Data Handling",l={},c=[{value:"Overview of Web Applications that can benefit from RxDB",id:"overview-of-web-applications-that-can-benefit-from-rxdb",level:2},{value:"Importance of data bases in Mobile Applications",id:"importance-of-data-bases-in-mobile-applications",level:2},{value:"Introducing RxDB as a data base Solution",id:"introducing-rxdb-as-a-data-base-solution",level:2},{value:"Getting Started with RxDB",id:"getting-started-with-rxdb",level:2},{value:"What is RxDB?",id:"what-is-rxdb",level:3},{value:"Reactive Data Handling",id:"reactive-data-handling",level:3},{value:"Offline-First Approach",id:"offline-first-approach",level:3},{value:"Data Replication",id:"data-replication",level:3},{value:"Observable Queries",id:"observable-queries",level:3},{value:"Multi-Tab support",id:"multi-tab-support",level:3},{value:"RxDB vs. Other data base Options",id:"rxdb-vs-other-data-base-options",level:3},{value:"Different RxStorage layers for RxDB",id:"different-rxstorage-layers-for-rxdb",level:3},{value:"Synchronizing Data with RxDB between Clients and Servers",id:"synchronizing-data-with-rxdb-between-clients-and-servers",level:2},{value:"Offline-First Approach",id:"offline-first-approach-1",level:3},{value:"RxDB Replication Plugins",id:"rxdb-replication-plugins",level:3},{value:"Advanced RxDB Features and Techniques",id:"advanced-rxdb-features-and-techniques",level:3},{value:"Encryption of Local Data",id:"encryption-of-local-data",level:3},{value:"Change Streams and Event Handling",id:"change-streams-and-event-handling",level:3},{value:"JSON Key Compression",id:"json-key-compression",level:3},{value:"Conclusion",id:"conclusion",level:2}];function d(e){const a={a:"a",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(a.header,{children:(0,t.jsx)(a.h1,{id:"rxdb-as-a-data-base-empowering-web-applications-with-reactive-data-handling",children:"RxDB as a data base: Empowering Web Applications with Reactive Data Handling"})}),"\n",(0,t.jsx)(a.p,{children:"In the world of web applications, efficient data management plays a crucial role in delivering a seamless user experience. As mobile applications continue to dominate the digital landscape, the importance of robust data bases becomes evident. In this article, we will explore RxDB as a powerful data base solution for web applications. We will delve into its features, advantages, and advanced techniques, highlighting its ability to handle reactive data and enable an offline-first approach."}),"\n",(0,t.jsx)("center",{children:(0,t.jsx)("a",{href:"https://rxdb.info/",children:(0,t.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"Data Base",width:"240"})})}),"\n",(0,t.jsx)(a.h2,{id:"overview-of-web-applications-that-can-benefit-from-rxdb",children:"Overview of Web Applications that can benefit from RxDB"}),"\n",(0,t.jsx)(a.p,{children:"Before diving into the specifics of RxDB, let's take a moment to understand the scope of web applications that can leverage its capabilities. Any web application that requires real-time data updates, offline functionality, and synchronization between clients and servers can greatly benefit from RxDB. Whether it's a collaborative document editing tool, a task management app, or a chat application, RxDB offers a robust foundation for building these types of applications."}),"\n",(0,t.jsx)(a.h2,{id:"importance-of-data-bases-in-mobile-applications",children:"Importance of data bases in Mobile Applications"}),"\n",(0,t.jsx)(a.p,{children:"Mobile applications have become an integral part of our lives, providing us with instant access to information and services. Behind the scenes, data bases play a pivotal role in storing and managing the data that powers these applications. data bases enable efficient data retrieval, updates, and synchronization, ensuring a smooth user experience even in challenging network conditions."}),"\n",(0,t.jsx)(a.h2,{id:"introducing-rxdb-as-a-data-base-solution",children:"Introducing RxDB as a data base Solution"}),"\n",(0,t.jsx)(a.p,{children:"RxDB, short for Reactive data base, is a client-side data base solution designed specifically for web and mobile applications. Built on the principles of reactive programming, RxDB brings the power of observables and event-driven architecture to data management. With RxDB, developers can create applications that are responsive, offline-ready, and capable of seamless data synchronization between clients and servers."}),"\n",(0,t.jsx)(a.h2,{id:"getting-started-with-rxdb",children:"Getting Started with RxDB"}),"\n",(0,t.jsx)(a.h3,{id:"what-is-rxdb",children:"What is RxDB?"}),"\n",(0,t.jsx)(a.p,{children:"RxDB is an open-source JavaScript data base that leverages reactive programming and provides a seamless API for handling data. It is built on top of existing popular data base technologies, such as IndexedDB, and adds a layer of reactive features to enable real-time data updates and synchronization."}),"\n",(0,t.jsx)(a.h3,{id:"reactive-data-handling",children:"Reactive Data Handling"}),"\n",(0,t.jsx)(a.p,{children:"One of the standout features of RxDB is its reactive data handling. It utilizes observables to provide a stream of data that automatically updates whenever a change occurs. This reactive approach allows developers to build applications that respond instantly to data changes, ensuring a highly interactive and real-time user experience."}),"\n",(0,t.jsx)(a.h3,{id:"offline-first-approach",children:"Offline-First Approach"}),"\n",(0,t.jsx)(a.p,{children:"RxDB embraces an offline-first approach, enabling applications to work seamlessly even when there is no internet connectivity. It achieves this by caching data locally on the client-side and synchronizing it with the server when the connection is available. This ensures that users can continue working with the application and have their data automatically synchronized when they come back online."}),"\n",(0,t.jsx)(a.h3,{id:"data-replication",children:"Data Replication"}),"\n",(0,t.jsx)(a.p,{children:"RxDB simplifies the process of data replication between clients and servers. It provides replication plugins that handle the synchronization of data in real-time. These plugins allow applications to keep data consistent across multiple clients, enabling collaborative features and ensuring that each client has the most up-to-date information."}),"\n",(0,t.jsx)(a.h3,{id:"observable-queries",children:"Observable Queries"}),"\n",(0,t.jsx)(a.p,{children:"RxDB introduces the concept of observable queries, which are powerful tools for efficiently querying data. With observable queries, developers can subscribe to specific data queries and receive automatic updates whenever the underlying data changes. This eliminates the need for manual polling and ensures that applications always have access to the latest data."}),"\n",(0,t.jsx)(a.h3,{id:"multi-tab-support",children:"Multi-Tab support"}),"\n",(0,t.jsxs)(a.p,{children:["RxDB offers multi-tab support, allowing applications to function seamlessly across multiple ",(0,t.jsx)(a.a,{href:"/articles/browser-database.html",children:"browser"})," tabs. This feature ensures that data changes in one tab are immediately reflected in all other open tabs, enabling a consistent user experience across different browser windows."]}),"\n",(0,t.jsx)(a.h3,{id:"rxdb-vs-other-data-base-options",children:"RxDB vs. Other data base Options"}),"\n",(0,t.jsxs)(a.p,{children:["When considering data base options for web applications, developers often encounter choices like IndexedDB, ",(0,t.jsx)(a.a,{href:"/rx-storage-opfs.html",children:"OPFS"}),", and Memory-based solutions. RxDB, while built on top of IndexedDB, stands out due to its reactive data handling capabilities and advanced synchronization features. Compared to other options, RxDB offers a more streamlined and powerful approach to managing data in web applications."]}),"\n",(0,t.jsx)(a.h3,{id:"different-rxstorage-layers-for-rxdb",children:"Different RxStorage layers for RxDB"}),"\n",(0,t.jsxs)(a.p,{children:["RxDB provides various ",(0,t.jsx)(a.a,{href:"/rx-storage.html",children:"storage layers"}),", known as RxStorage, that serve as interfaces to different underlying ",(0,t.jsx)(a.a,{href:"/articles/browser-storage.html",children:"storage"})," technologies. These layers include:"]}),"\n",(0,t.jsxs)(a.ul,{children:["\n",(0,t.jsxs)(a.li,{children:[(0,t.jsx)(a.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"}),": Built on top of the browsers ",(0,t.jsx)(a.a,{href:"/articles/localstorage.html",children:"localStorage API"}),"."]}),"\n",(0,t.jsxs)(a.li,{children:[(0,t.jsx)(a.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),": This layer directly utilizes IndexedDB as its backend, providing a robust and widely supported storage option."]}),"\n",(0,t.jsxs)(a.li,{children:[(0,t.jsx)(a.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"}),": OPFS (Operational Transformation File System) is a file system-like storage layer that allows for efficient conflict resolution and real-time collaboration."]}),"\n",(0,t.jsx)(a.li,{children:"Memory RxStorage: Primarily used for testing and development, this storage layer keeps data in memory without persisting it to disk.\nEach RxStorage layer has its strengths and is suited for different scenarios, enabling developers to choose the most appropriate option for their specific use case."}),"\n"]}),"\n",(0,t.jsx)(a.h2,{id:"synchronizing-data-with-rxdb-between-clients-and-servers",children:"Synchronizing Data with RxDB between Clients and Servers"}),"\n",(0,t.jsx)(a.h3,{id:"offline-first-approach-1",children:"Offline-First Approach"}),"\n",(0,t.jsx)(a.p,{children:"As mentioned earlier, RxDB adopts an offline-first approach, allowing applications to function seamlessly in disconnected environments. By caching data locally, applications can continue to operate and make updates even without an internet connection. Once the connection is restored, RxDB's replication plugins take care of synchronizing the data with the server, ensuring consistency across all clients."}),"\n",(0,t.jsx)(a.h3,{id:"rxdb-replication-plugins",children:"RxDB Replication Plugins"}),"\n",(0,t.jsx)(a.p,{children:"RxDB provides a range of replication plugins that simplify the process of synchronizing data between clients and servers. These plugins enable real-time replication using various protocols, such as WebSocket or HTTP, and handle conflict resolution strategies to ensure data integrity. By leveraging these replication plugins, developers can easily implement robust and scalable synchronization capabilities in their applications."}),"\n",(0,t.jsx)(a.h3,{id:"advanced-rxdb-features-and-techniques",children:"Advanced RxDB Features and Techniques"}),"\n",(0,t.jsx)(a.p,{children:"Indexing and Performance Optimization\nTo achieve optimal performance, RxDB offers indexing capabilities. Indexing allows for efficient data retrieval and faster query execution. By strategically defining indexes on frequently accessed fields, developers can significantly enhance the overall performance of their RxDB-powered applications."}),"\n",(0,t.jsx)(a.h3,{id:"encryption-of-local-data",children:"Encryption of Local Data"}),"\n",(0,t.jsx)(a.p,{children:"In scenarios where data security is paramount, RxDB provides options for encrypting local data. By encrypting the data base contents, developers can ensure that sensitive information remains secure even if the underlying storage is compromised. RxDB integrates seamlessly with encryption libraries, making it easy to implement end-to-end encryption in applications."}),"\n",(0,t.jsx)(a.h3,{id:"change-streams-and-event-handling",children:"Change Streams and Event Handling"}),"\n",(0,t.jsx)(a.p,{children:"RxDB offers change streams and event handling mechanisms, enabling developers to react to data changes in real-time. With change streams, applications can listen to specific collections or documents and trigger custom logic whenever a change occurs. This capability opens up possibilities for building real-time collaboration features, notifications, or other reactive behaviors."}),"\n",(0,t.jsx)(a.h3,{id:"json-key-compression",children:"JSON Key Compression"}),"\n",(0,t.jsxs)(a.p,{children:["In scenarios where storage size is a concern, RxDB provides JSON ",(0,t.jsx)(a.a,{href:"/key-compression.html",children:"key compression"}),". By applying compression techniques to JSON keys, developers can significantly reduce the storage footprint of their data bases. This feature is particularly beneficial for applications dealing with large datasets or ",(0,t.jsx)(a.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"limited storage capacities"}),"."]}),"\n",(0,t.jsx)(a.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,t.jsxs)(a.p,{children:["RxDB provides an exceptional data base solution for web and mobile applications, empowering developers to create reactive, offline-ready, and synchronized applications. With its reactive data handling, offline-first approach, and replication plugins, RxDB simplifies the challenges of building real-time applications with data synchronization requirements. By embracing advanced features like indexing, encryption, change streams, and JSON key compression, developers can optimize performance, enhance security, and reduce storage requirements. As web and ",(0,t.jsx)(a.a,{href:"/articles/mobile-database.html",children:"mobile applications"})," continue to evolve, RxDB proves to be a reliable and powerful"]}),"\n",(0,t.jsx)("center",{children:(0,t.jsx)("a",{href:"https://rxdb.info/",children:(0,t.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"Data Base",width:"240"})})})]})}function h(e={}){const{wrapper:a}={...(0,s.R)(),...e.components};return a?(0,t.jsx)(a,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453(e,a,i){i.d(a,{R:()=>r,x:()=>o});var n=i(6540);const t={},s=n.createContext(t);function r(e){const a=n.useContext(s);return n.useMemo(function(){return"function"==typeof e?e(a):{...a,...e}},[a,e])}function o(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),n.createElement(s.Provider,{value:a},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/393be207.9cd944e0.js b/docs/assets/js/393be207.9cd944e0.js
deleted file mode 100644
index 28d4f385533..00000000000
--- a/docs/assets/js/393be207.9cd944e0.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4134],{591(e,n,t){t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>p,default:()=>l,frontMatter:()=>s,metadata:()=>a,toc:()=>d});const a=JSON.parse('{"type":"mdx","permalink":"/markdown-page","source":"@site/src/pages/markdown-page.md","title":"Markdown page example","description":"You don\'t need React to write simple standalone pages.","frontMatter":{"title":"Markdown page example"},"unlisted":false}');var o=t(4848),r=t(8453);const s={title:"Markdown page example"},p="Markdown page example",c={},d=[];function i(e){const n={h1:"h1",header:"header",p:"p",...(0,r.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"markdown-page-example",children:"Markdown page example"})}),"\n",(0,o.jsx)(n.p,{children:"You don't need React to write simple standalone pages."})]})}function l(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(i,{...e})}):i(e)}},8453(e,n,t){t.d(n,{R:()=>s,x:()=>p});var a=t(6540);const o={},r=a.createContext(o);function s(e){const n=a.useContext(r);return a.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function p(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),a.createElement(r.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/39600c95.6a7491bc.js b/docs/assets/js/39600c95.6a7491bc.js
deleted file mode 100644
index 1fa9f7307fa..00000000000
--- a/docs/assets/js/39600c95.6a7491bc.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3148],{8228(e,n,s){s.r(n),s.d(n,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"rx-query","title":"RxQuery","description":"Master RxQuery in RxDB - find, update, remove documents using Mango syntax, chained queries, real-time observations, indexing, and more.","source":"@site/docs/rx-query.md","sourceDirName":".","slug":"/rx-query.html","permalink":"/rx-query.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxQuery","slug":"rx-query.html","description":"Master RxQuery in RxDB - find, update, remove documents using Mango syntax, chained queries, real-time observations, indexing, and more."},"sidebar":"tutorialSidebar","previous":{"title":"RxDocument","permalink":"/rx-document.html"},"next":{"title":"RxStorage Overview","permalink":"/rx-storage.html"}}');var o=s(4848),i=s(8453);const a={title:"RxQuery",slug:"rx-query.html",description:"Master RxQuery in RxDB - find, update, remove documents using Mango syntax, chained queries, real-time observations, indexing, and more."},l="RxQuery",t={},c=[{value:"find()",id:"find",level:2},{value:"findOne()",id:"findone",level:2},{value:"exec()",id:"exec",level:2},{value:"Observe $",id:"observe-",level:2},{value:"update()",id:"update",level:2},{value:"patch() / incrementalPatch()",id:"patch--incrementalpatch",level:2},{value:"modify() / incrementalModify()",id:"modify--incrementalmodify",level:2},{value:"remove() / incrementalRemove()",id:"remove--incrementalremove",level:2},{value:"doesDocumentDataMatch()",id:"doesdocumentdatamatch",level:2},{value:"Query Builder Plugin",id:"query-builder-plugin",level:2},{value:"Query Examples",id:"query-examples",level:2},{value:"Setting a specific index",id:"setting-a-specific-index",level:2},{value:"Count",id:"count",level:2},{value:"allowSlowCount",id:"allowslowcount",level:3},{value:"RxQuery's are immutable",id:"rxquerys-are-immutable",level:2},{value:"isRxQuery",id:"isrxquery",level:3},{value:"Design Decisions",id:"design-decisions",level:2},{value:"FAQ",id:"faq",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...e.components},{Details:s}=n;return s||function(e,n){throw new Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"rxquery",children:"RxQuery"})}),"\n",(0,o.jsxs)(n.p,{children:["To find documents inside of an ",(0,o.jsx)(n.a,{href:"/rx-collection.html",children:"RxCollection"}),", RxDB uses the RxQuery interface that handles all query operations: it serves as the main interface for fetching documents, relies on a MongoDB-like ",(0,o.jsx)(n.a,{href:"https://github.com/cloudant/mango",children:"Mango Query Syntax"}),", and provides three types of queries: ",(0,o.jsx)(n.a,{href:"#find",children:"find()"}),", ",(0,o.jsx)(n.a,{href:"#findone",children:"findOne()"})," and ",(0,o.jsx)(n.a,{href:"#count",children:"count()"}),". By caching and de-duplicating results, RxQuery ensures efficient in-memory handling, and when queries are observed or re-run, the ",(0,o.jsx)(n.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce algorithm"})," speeds up updates for a fast real-time experience and queries that run more than once."]}),"\n",(0,o.jsx)(n.h2,{id:"find",children:"find()"}),"\n",(0,o.jsxs)(n.p,{children:["To create a basic ",(0,o.jsx)(n.code,{children:"RxQuery"}),", call ",(0,o.jsx)(n.code,{children:".find()"})," on a collection and insert selectors. The result-set of normal queries is an array with documents."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// find all that are older then 18"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCollection"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"findone",children:"findOne()"}),"\n",(0,o.jsxs)(n.p,{children:["A findOne-query has only a single ",(0,o.jsx)(n.code,{children:"RxDocument"})," or ",(0,o.jsx)(n.code,{children:"null"})," as result-set."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// find alice"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCollection"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'alice'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})})]})})}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// find the youngest one"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCollection"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})})]})})}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// find one document by the primary key"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"exec",children:"exec()"}),"\n",(0,o.jsxs)(n.p,{children:["Returns a ",(0,o.jsx)(n.code,{children:"Promise"})," that resolves with the result-set of the query."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" results"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(results); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// > [RxDocument,RxDocument,RxDocument..]"})]})]})})}),"\n",(0,o.jsxs)(n.p,{children:["On ",(0,o.jsx)(n.code,{children:".findOne()"})," queries, you can call ",(0,o.jsx)(n.code,{children:".exec(true)"})," to ensure your document exists and to make TypeScript handling easier:"]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// docOrUndefined can be the type RxDocument or null which then has to be handled to be typesafe."})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docOrUndefined"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// with .exec(true), it will throw if the document cannot be found and always have the type RxDocument"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"observe-",children:"Observe $"}),"\n",(0,o.jsxs)(n.p,{children:["An ",(0,o.jsx)(n.code,{children:"BehaviorSubject"})," ",(0,o.jsx)(n.a,{href:"https://medium.com/@luukgruijs/understanding-rxjs-behaviorsubject-replaysubject-and-asyncsubject-8cc061f1cfc0",children:"see"})," that always has the current result-set as value.\nThis is extremely helpful when used together with UIs that should always show the same state as what is written in the database."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" querySub"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(results "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'got results: '"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" results"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// > 'got results: 5' // BehaviorSubjects emit on subscription"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert one"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// > 'got results: 6' // $.subscribe() was called again with the new results"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// stop watching this query"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"querySub"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".unsubscribe"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"update",children:"update()"}),"\n",(0,o.jsxs)(n.p,{children:["Runs an ",(0,o.jsx)(n.a,{href:"/rx-document.html#update",children:"update"})," on every RxDocument of the query-result."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// to use the update() method, you need to add the update plugin."})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBUpdatePlugin } "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/update'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBUpdatePlugin);"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".update"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $inc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // increases age of every found document by 1"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"patch--incrementalpatch",children:"patch() / incrementalPatch()"}),"\n",(0,o.jsxs)(n.p,{children:["Runs the ",(0,o.jsx)(n.a,{href:"/rx-document.html#patch",children:"RxDocument.patch()"})," function on every RxDocument of the query result."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".patch"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 12"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // set the age of every found to 12"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"modify--incrementalmodify",children:"modify() / incrementalModify()"}),"\n",(0,o.jsxs)(n.p,{children:["Runs the ",(0,o.jsx)(n.a,{href:"/rx-document.html#modify",children:"RxDocument.modify()"})," function on every RxDocument of the query result."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".modify"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"((docData) "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docData"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".age "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docData"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".age "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"; "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// increases age of every found document by 1"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docData;"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"remove--incrementalremove",children:"remove() / incrementalRemove()"}),"\n",(0,o.jsx)(n.p,{children:"Deletes all found documents. Returns a promise which resolves to the deleted documents."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// All documents where the age is less than 18"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $lt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Remove the documents from the collection"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" removedDocs"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"doesdocumentdatamatch",children:"doesDocumentDataMatch()"}),"\n",(0,o.jsxs)(n.p,{children:["Returns ",(0,o.jsx)(n.code,{children:"true"})," if the given document data matches the query."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" documentData"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 19"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".doesDocumentDataMatch"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documentData); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// > true"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 20"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".doesDocumentDataMatch"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documentData); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// > false"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"query-builder-plugin",children:"Query Builder Plugin"}),"\n",(0,o.jsxs)(n.p,{children:["To use chained query methods, you can also use the ",(0,o.jsx)(n.code,{children:"query-builder"})," plugin."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// add the query builder plugin"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBQueryBuilderPlugin } "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/query-builder'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBQueryBuilderPlugin);"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// now you can use chained query methods"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".where"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'age'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"18"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"query-examples",children:"Query Examples"}),"\n",(0,o.jsx)(n.p,{children:"Here some examples to fast learn how to write queries without reading the docs."}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.a,{href:"https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-find/README.md",children:"Pouch-find-docs"})," - learn how to use mango-queries"]}),"\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.a,{href:"https://github.com/aheckmann/mquery/blob/master/README.md",children:"mquery-docs"})," - learn how to use chained-queries"]}),"\n"]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// directly pass search-object"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $eq"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents));"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/*"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * find by using sql equivalent '%like%' syntax"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This example will fe: match 'foo' but also 'fifoo' or 'foofa' or 'fifoofa'"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Notice that in RxDB queries, a regex is represented as a $regex string with the $options parameter for flags."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Using a RegExp instance is not allowed because they are not JSON.stringify()-able and also"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * RegExp instances are mutable which could cause undefined behavior when the RegExp is mutated"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * after the query was parsed."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $regex"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '.*foo.*'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents));"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// find using a composite statement eg: $or"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// This example checks where name is either foo or if name is not existent on the document"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $or"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" [ { name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $eq"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $exists"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } }] }"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents));"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// do a case insensitive search"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// This example will match 'foo' or 'FOO' or 'FoO' etc..."})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $regex"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '^foo$'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $options"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'i'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } }"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents));"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// chained queries"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".where"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'name'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".eq"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foo'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documents));"})]})]})})}),"\n",(0,o.jsx)(n.admonition,{title:"RxDB will always append the primary key to the sort parameters",type:"note",children:(0,o.jsxs)(n.p,{children:["For several performance optimizations, like the ",(0,o.jsx)(n.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce algorithm"}),", RxDB expects all queries to return a deterministic sort order that does not depend on the insert order of the documents. To ensure a deterministic ordering, RxDB will always append the primary key as last sort parameter to all queries and to all indexes.\nThis works in contrast to most other databases where a query without sorting would return the documents in the order in which they had been inserted to the database."]})}),"\n",(0,o.jsx)(n.h2,{id:"setting-a-specific-index",children:"Setting a specific index"}),"\n",(0,o.jsx)(n.p,{children:"By default, the query will be sent to the RxStorage, where a query planner will determine which one of the available indexes must be used.\nBut the query planner cannot know everything and sometimes will not pick the most optimal index.\nTo improve query performance, you can specify which index must be used, when running the query."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCollection"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" gender"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $eq"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'm'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Because the developer knows that 50% of the documents are 'male',"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * but only 20% are below age 18,"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * it makes sense to enforce using the ['gender', 'age'] index to improve performance."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This could not be known by the query planer which might have chosen ['age', 'gender'] instead."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" index"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'gender'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'age'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"count",children:"Count"}),"\n",(0,o.jsxs)(n.p,{children:["When you only need the amount of documents that match a query, but you do not need the document data itself, you can use a count query for ",(0,o.jsx)(n.strong,{children:"better performance"}),".\nThe performance difference compared to a normal query differs depending on which ",(0,o.jsx)(n.a,{href:"/rx-storage.html",children:"RxStorage"})," implementation is used."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".count"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 18"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // 'limit' and 'skip' MUST NOT be set for count queries."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// get the count result once"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" matchingAmount"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// > number"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// observe the result"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(amount "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Currently has '"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" amount "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ' documents'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(n.admonition,{type:"note",children:(0,o.jsxs)(n.p,{children:["Count queries have a better performance than normal queries because they do not have to fetch the full document data out of the storage. Therefore it is ",(0,o.jsx)(n.strong,{children:"not"})," possible to run a ",(0,o.jsx)(n.code,{children:"count()"})," query with a selector that requires to fetch and compare the document data. So if your query selector ",(0,o.jsx)(n.strong,{children:"does not"})," fully match an index of the schema, it is not allowed to run it. These queries would have no performance benefit compared to normal queries but have the tradeoff of not using the fetched document data for caching."]})}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The following will throw an error because"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * the count operation cannot run on any specific index range"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * because the $regex operator is used."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".count"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $regex"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The following will throw an error because"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * the count operation cannot run on any specific index range"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * because there is no ['age' ,'otherNumber'] index"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * defined in the schema."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".count"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 20"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" otherNumber"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(n.p,{children:"If you want to count these kinds of queries, you should do a normal query instead and use the length of the result set as counter. This has the same performance as running a non-fully-indexed count which has to fetch all document data from the database and run a query matcher."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// get count manually once"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" resultSet"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $regex"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" count"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" resultSet"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// observe count manually"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" count$"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $regex"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".pipe"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" map"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(result "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * To allow non-fully-indexed count queries,"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * you can also specify that by setting allowSlowCount=true"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * when creating the database."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" allowSlowCount"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // set this to true [default=false]"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(n.h3,{id:"allowslowcount",children:(0,o.jsx)(n.code,{children:"allowSlowCount"})}),"\n",(0,o.jsxs)(n.p,{children:["To allow non-fully-indexed count queries, you can also specify that by setting ",(0,o.jsx)(n.code,{children:"allowSlowCount: true"})," when creating the database.\nDoing this is mostly not wanted, because it would run the counting on the storage without having the document stored in the RxDB document cache.\nThis is only recommended if the RxStorage is running remotely like in a WebWorker and you not always want to send the document-data between the worker and the main thread. In this case you might only need the count-result instead to save performance."]}),"\n",(0,o.jsx)(n.h2,{id:"rxquerys-are-immutable",children:"RxQuery's are immutable"}),"\n",(0,o.jsxs)(n.p,{children:["Because RxDB is a reactive database, we can do heavy performance-optimisation on query-results which change over time. To be able to do this, RxQuery's have to be immutable.\nThis means, when you have a ",(0,o.jsx)(n.code,{children:"RxQuery"})," and run a ",(0,o.jsx)(n.code,{children:".where()"})," on it, the original RxQuery-Object is not changed. Instead the where-function returns a new ",(0,o.jsx)(n.code,{children:"RxQuery"}),"-Object with the changed where-field. Keep this in mind if you create RxQuery's and change them afterwards."]}),"\n",(0,o.jsx)(n.p,{children:"Example:"}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" queryObject"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".where"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'age'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".gt"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"18"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Creates a new RxQuery object, does not modify previous one"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"queryObject"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".sort"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'name'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" results"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" queryObject"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(results); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// result-documents are not sorted by name"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" queryObjectSort"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" queryObject"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".sort"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'name'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" results"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" queryObjectSort"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(results); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// result-documents are now sorted"})]})]})})}),"\n",(0,o.jsx)(n.h3,{id:"isrxquery",children:"isRxQuery"}),"\n",(0,o.jsx)(n.p,{children:"Returns true if the given object is an instance of RxQuery. Returns false if not."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsx)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" is"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" isRxQuery"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(myObj);"})]})})})}),"\n",(0,o.jsx)(n.h2,{id:"design-decisions",children:"Design Decisions"}),"\n",(0,o.jsxs)(n.p,{children:["Like most other noSQL-Databases, RxDB uses the ",(0,o.jsx)(n.a,{href:"https://github.com/cloudant/mango",children:"mango-query-syntax"})," similar to MongoDB and others."]}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["We use the JSON based Mango Query Syntax because:","\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Mango Queries work better with TypeScript compared to SQL strings."}),"\n",(0,o.jsx)(n.li,{children:"Mango Queries are composable and easy to transform by code without joining SQL strings."}),"\n",(0,o.jsx)(n.li,{children:"Queries can be run very fast and efficient with only a minimal query planer to plan the best indexes and operations."}),"\n",(0,o.jsxs)(n.li,{children:["NoSQL queries can be optimized with the ",(0,o.jsx)(n.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce"})," algorithm to improve performance of observed and cached queries."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"faq",children:"FAQ"}),"\n",(0,o.jsxs)(s,{children:[(0,o.jsx)("summary",{children:"Can I specify which document fields are returned by an RxDB query?"}),(0,o.jsx)("div",{children:(0,o.jsx)(n.p,{children:"No, RxDB does not support partial document retrieval. Because RxDB is a client-side database with limited memory, it caches and de-duplicates entire documents across multiple queries. Even if you only need a few fields, most storages must still fetch the entire JSON data, so subselecting fields would not significantly improve performance. Therefore, RxDB always returns full documents. If you only need certain fields, you can filter them out in your application code or consider storing just the necessary data in a separate collection."})})]}),"\n",(0,o.jsxs)(s,{children:[(0,o.jsx)("summary",{children:"Why doesn't RxDB support aggregations on queries?"}),(0,o.jsx)("div",{children:(0,o.jsx)(n.p,{children:"RxDB runs entirely on the client side. Any \"aggregation\" or data processing you might do within RxDB would still happen in the same JavaScript environment as your application code. Therefore, there's no real performance advantage or difference between doing the aggregation in RxDB vs. doing it in your own code after fetching the data. As a result, RxDB doesn't provide built-in aggregation methods. Instead, just query the documents you need and perform any calculations directly in your app's code."})})]}),"\n",(0,o.jsxs)(s,{children:[(0,o.jsx)("summary",{children:"Why does RxDB not support cross-collection queries?"}),(0,o.jsx)("div",{children:(0,o.jsx)(n.p,{children:"RxDB is a client-side database and does not provide built-in cross-collection queries or transactions. Instead, you can execute multiple queries in your JavaScript code and combine their results as needed. Because everything runs in the same environment, this approach offers the same performance you would get if cross-collection queries were built in - without the added complexity."})})]}),"\n",(0,o.jsxs)(s,{children:[(0,o.jsx)("summary",{children:"Why Doesn't RxDB Support Case-Insensitive Search?"}),(0,o.jsxs)("div",{children:[(0,o.jsxs)(n.p,{children:["RxDB relies on various storage engines as its backend, and these storage engines generally do not support case-insensitive search natively, like ",(0,o.jsx)(n.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," or ",(0,o.jsx)(n.a,{href:"/rx-storage-foundationdb.html",children:"FoundationDB"}),". This limitation arises from the design of these engines, which prioritize efficiency and flexibility for specific types of queries rather than universal features like case-insensitivity. Although RxDB does not offer built-in support for case-insensitive search, there are two common workarounds:"]}),(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.strong,{children:"Store Data in a Meta-Field for Lowercase Search"}),": To enable case-insensitive search, you can store an additional field in your documents where the relevant text data is preprocessed and saved in lowercase."]}),"\n"]}),(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" document"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'John Doe'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" nameLowercase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'john doe'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Meta-field"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(document);"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" nameLowercase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $eq"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'john doe'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:[(0,o.jsx)(n.strong,{children:"Use a Regex Query"}),": Regular expressions can perform case-insensitive searches. For example:"]}),"\n"]}),(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { $regex"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '^john doe$'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $options"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'i'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Case-insensitive regex"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(n.p,{children:"However, this method has a significant downside: regex queries often cannot leverage indexes efficiently. As a result, they may be slower, especially for large datasets."})]})]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>a,x:()=>l});var r=s(6540);const o={},i=r.createContext(o);function a(e){const n=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/3ebfb37f.f5f42f4c.js b/docs/assets/js/3ebfb37f.f5f42f4c.js
deleted file mode 100644
index acc975ddb3c..00000000000
--- a/docs/assets/js/3ebfb37f.f5f42f4c.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[205],{6700(a,e,s){s.r(e),s.d(e,{default:()=>r});var l=s(544),n=s(4848);function r(){return(0,l.default)({sem:{id:"gads",metaTitle:"The local Database for Angular Apps",appName:"Angular",title:(0,n.jsxs)(n.Fragment,{children:["The easiest way to ",(0,n.jsx)("b",{children:"store"})," and ",(0,n.jsx)("b",{children:"sync"})," Data in Angular"]}),iconUrl:"/files/icons/angular.svg"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/401008a8.cfd81712.js b/docs/assets/js/401008a8.cfd81712.js
deleted file mode 100644
index ccc2415bc88..00000000000
--- a/docs/assets/js/401008a8.cfd81712.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5219],{3156(e,s,r){r.r(s),r.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>i,metadata:()=>n,toc:()=>d});const n=JSON.parse('{"id":"nodejs-database","title":"RxDB - The Real-Time Database for Node.js","description":"Discover how RxDB brings flexible, reactive NoSQL to Node.js. Scale effortlessly, persist data, and power your server-side apps with ease.","source":"@site/docs/nodejs-database.md","sourceDirName":".","slug":"/nodejs-database.html","permalink":"/nodejs-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB - The Real-Time Database for Node.js","slug":"nodejs-database.html","description":"Discover how RxDB brings flexible, reactive NoSQL to Node.js. Scale effortlessly, persist data, and power your server-side apps with ease."},"sidebar":"tutorialSidebar","previous":{"title":"Downsides of Local First / Offline First","permalink":"/downsides-of-offline-first.html"},"next":{"title":"Local First / Offline First","permalink":"/offline-first.html"}}');var o=r(4848),a=r(8453);const i={title:"RxDB - The Real-Time Database for Node.js",slug:"nodejs-database.html",description:"Discover how RxDB brings flexible, reactive NoSQL to Node.js. Scale effortlessly, persist data, and power your server-side apps with ease."},t="Node.js Database",l={},d=[{value:"Persistent Database",id:"persistent-database",level:2},{value:"RxDB as Node.js In-Memory Database",id:"rxdb-as-nodejs-in-memory-database",level:2},{value:"Hybrid In-memory-persistence-synced storage",id:"hybrid-in-memory-persistence-synced-storage",level:2},{value:"Share database between microservices with RxDB",id:"share-database-between-microservices-with-rxdb",level:2},{value:"Follow up on RxDB+Node.js",id:"follow-up-on-rxdbnodejs",level:2}];function c(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"nodejs-database",children:"Node.js Database"})}),"\n",(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.a,{href:"https://rxdb.info",children:"RxDB"})," is a fast, reactive realtime NoSQL ",(0,o.jsx)(s.strong,{children:"database"})," made for ",(0,o.jsx)(s.strong,{children:"JavaScript"})," applications like Websites, hybrid Apps, ",(0,o.jsx)(s.a,{href:"/electron-database.html",children:"Electron-Apps"}),", Progressive Web Apps and ",(0,o.jsx)(s.strong,{children:"Node.js"}),". While RxDB was initially created to be used with UI applications, it has been matured and optimized to make it useful for pure server-side use cases. It can be used as embedded, local database inside of the Node.js JavaScript process, or it can be used similar to a database server that Node.js can connect to. The ",(0,o.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," layer makes it possible to switch out the underlying storage engine which makes RxDB a very flexible database that can be optimized for many scenarios."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/icons/nodejs.svg",alt:"Node.js",width:"70"})}),"\n",(0,o.jsx)(s.h2,{id:"persistent-database",children:"Persistent Database"}),"\n",(0,o.jsxs)(s.p,{children:['To get a "normal" database connection where the data is persisted to a file system, the RxDB real time database provides multiple ',(0,o.jsx)(s.a,{href:"/rx-storage.html",children:"storage implementations"})," that work in Node.js."]}),"\n",(0,o.jsxs)(s.p,{children:["The ",(0,o.jsx)(s.a,{href:"/rx-storage-foundationdb.html",children:"FoundationDB"})," storage connects to a ",(0,o.jsx)(s.a,{href:"https://github.com/apple/foundationdb",children:"FoundationDB"})," cluster which itself is just a distributed key-value engine. RxDB adds the NoSQL query-engine, indexes and other features on top of it.\nIt scales horizontally because you can always add more servers to the FoundationDB cluster to increase the capacity.\nSetting up a RxDB database is pretty simple. You import the FoundationDB RxStorage and tell RxDB to use that when calling ",(0,o.jsx)(s.code,{children:"createRxDatabase"}),":"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageFoundationDB } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-foundationdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageFoundationDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" apiVersion"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 620"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" clusterFile"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/path/to/fdb.cluster'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// add a collection"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" users"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// run a query"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"users"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "})]})})}),"\n",(0,o.jsxs)(s.p,{children:["Another alternative storage is the ",(0,o.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," that stores the data inside of a SQLite filebased database. The SQLite storage is faster than FoundationDB and does not require to set up a cluster or anything because SQLite directly stores and reads the data inside of the filesystem. The downside of that is that it only scales vertically."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsNode"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqlite3 "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'sqlite3'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'path/to/database/file/foobar.db'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsNode"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite3)"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsxs)(s.p,{children:["Because the SQLite RxStorage is not free and you might not want to set up a FoundationDB cluster, there is also the option to use the ",(0,o.jsx)(s.a,{href:"/rx-storage-lokijs.html",children:"LokiJS RxStorage"})," together with the filesystem adapter. This will store the data as plain json in a file and load everything into memory on startup. This works great for small prototypes but it is not recommended to be used in production."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" LokiFsStructuredAdapter"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'lokijs/src/loki-fs-structured-adapter.js'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLoki } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-lokijs'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqlite3 "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'sqlite3'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'path/to/database/file/foobar.db'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLoki"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" adapter"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" LokiFsStructuredAdapter"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.p,{children:"Here is a performance comparison chart of the different storages (lower is better):"}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/rx-storage-performance-node.png",alt:"database performance - Node.js",width:"700"})}),"\n",(0,o.jsx)(s.h2,{id:"rxdb-as-nodejs-in-memory-database",children:"RxDB as Node.js In-Memory Database"}),"\n",(0,o.jsxs)(s.p,{children:["One of the easiest way to use RxDB in Node.js is to use the ",(0,o.jsx)(s.a,{href:"/rx-storage-memory.html",children:"Memory RxStorage"}),". As the name implies, it stores the data directly ",(0,o.jsx)(s.strong,{children:"in-memory"})," of the Node.js JavaScript process. This makes it really fast to read and write data but of course the data is not persisted and will be lost when the nodejs process exits. Often the in-memory option is used when RxDB is used in unit tests because it automatically cleans up everything afterwards."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageMemory } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-memory'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMemory"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsxs)(s.p,{children:["Also notice that the ",(0,o.jsx)(s.a,{href:"https://medium.com/geekculture/node-js-default-memory-settings-3c0fe8a9ba1",children:"default memory limit"})," of Node.js is 4gb (might change of newer versions) so for bigger datasets you might want to increase the limit with the ",(0,o.jsx)(s.code,{children:"max-old-space-size"})," flag:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"# increase the Node.js memory limit to 8GB"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"node"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" --max-old-space-size=8192"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" index.js"})]})]})})}),"\n",(0,o.jsx)(s.h2,{id:"hybrid-in-memory-persistence-synced-storage",children:"Hybrid In-memory-persistence-synced storage"}),"\n",(0,o.jsxs)(s.p,{children:["If you want to have the performance of an ",(0,o.jsx)(s.strong,{children:"in-memory database"})," but require persistency of the data, you can use the ",(0,o.jsx)(s.a,{href:"/rx-storage-memory-mapped.html",children:"memory-mapped storage"}),". On database creation it will load all data into the memory and on writes it will first write the data into memory and later also write it to the persistent storage in the background. In the following example the FoundationDB storage is used, but any other RxStorage can be used as persistence layer."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageFoundationDB } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-foundationdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getMemoryMappedRxStorage } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-memory-mapped'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getMemoryMappedRxStorage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageFoundationDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" apiVersion"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 620"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" clusterFile"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/path/to/fdb.cluster'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.p,{children:"While this approach gives you a database with great performance and persistent, it has two major downsides:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsx)(s.li,{children:"The database size is limited to the memory size"}),"\n",(0,o.jsx)(s.li,{children:"Writes can be lost when the Node.js process exists between a write to the memory state and the background persisting."}),"\n"]}),"\n",(0,o.jsx)(s.h2,{id:"share-database-between-microservices-with-rxdb",children:"Share database between microservices with RxDB"}),"\n",(0,o.jsxs)(s.p,{children:["Using a local, embedded database in Node.js works great until you have to share the data with another Node.js process or another server at all.\nTo share the database state with other instances, RxDB provides two different methods. One is ",(0,o.jsx)(s.a,{href:"/replication.html",children:"replication"})," and the other is the ",(0,o.jsx)(s.a,{href:"/rx-storage-remote.html",children:"remote RxStorage"}),'.\nThe replication copies over the whole database set to other instances live-replicates all ongoing writes. This has the benefit of scaling better because each of your microservice will run queries on its own copy of the dataset.\nSometimes however you might not want to store the full dataset on each microservice. Then it is better to use the remote RxStorage and connect it to the "main" database. The remote storage will run all operations the main database and return the result to the calling database.']}),"\n",(0,o.jsx)(s.h2,{id:"follow-up-on-rxdbnodejs",children:"Follow up on RxDB+Node.js"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:["Check out the ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/node",children:"RxDB Nodejs example"}),"."]}),"\n",(0,o.jsxs)(s.li,{children:["If you haven't done yet, you should start learning about RxDB with the ",(0,o.jsx)(s.a,{href:"/quickstart.html",children:"Quickstart Tutorial"}),"."]}),"\n",(0,o.jsxs)(s.li,{children:["I created ",(0,o.jsx)(s.a,{href:"/alternatives.html",children:"a list of embedded JavaSCript databases"})," that you will help you to pick a database if you do not want to use RxDB."]}),"\n",(0,o.jsxs)(s.li,{children:["Check out the ",(0,o.jsx)(s.a,{href:"/rx-storage-mongodb.html",children:"MongoDB RxStorage"})," that uses MongoDB for the database connection from your Node.js application and runs the RxDB real time database on top of it."]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(c,{...e})}):c(e)}},8453(e,s,r){r.d(s,{R:()=>i,x:()=>t});var n=r(6540);const o={},a=n.createContext(o);function i(e){const s=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),n.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/40b6398a.86580fca.js b/docs/assets/js/40b6398a.86580fca.js
deleted file mode 100644
index 020a8ae7b1f..00000000000
--- a/docs/assets/js/40b6398a.86580fca.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9594],{3247(e,s,n){n.d(s,{g:()=>i});var r=n(4848);function i(e){const s=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&s.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&s.push(n),(0,r.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:s+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,r.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},6375(e,s,n){n.r(s),n.d(s,{assets:()=>p,contentTitle:()=>h,default:()=>j,frontMatter:()=>d,metadata:()=>r,toc:()=>x});const r=JSON.parse('{"id":"replication-supabase","title":"Supabase Replication Plugin for RxDB - Real-Time, Offline-First Sync","description":"Build real-time, offline-capable apps with RxDB + Supabase. Push/pull changes via PostgREST, stream updates with Realtime, and keep data in sync across devices.","source":"@site/docs/replication-supabase.md","sourceDirName":".","slug":"/replication-supabase.html","permalink":"/replication-supabase.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Supabase Replication Plugin for RxDB - Real-Time, Offline-First Sync","slug":"replication-supabase.html","description":"Build real-time, offline-capable apps with RxDB + Supabase. Push/pull changes via PostgREST, stream updates with Realtime, and keep data in sync across devices."},"sidebar":"tutorialSidebar","previous":{"title":"MongoDB Replication","permalink":"/replication-mongodb.html"},"next":{"title":"NATS Replication","permalink":"/replication-nats.html"}}');var i=n(4848),o=n(8453),a=n(2636),l=n(3247),t=n(2271),c=n(7482);const d={title:"Supabase Replication Plugin for RxDB - Real-Time, Offline-First Sync",slug:"replication-supabase.html",description:"Build real-time, offline-capable apps with RxDB + Supabase. Push/pull changes via PostgREST, stream updates with Realtime, and keep data in sync across devices."},h="Supabase Replication Plugin for RxDB - Real-Time, Offline-First Sync",p={},x=[{value:"Key Features of the RxDB-Supabase Plugin",id:"key-features-of-the-rxdb-supabase-plugin",level:2},{value:"Architecture Overview",id:"architecture-overview",level:2},{value:"Setting up RxDB \u2194 Supabase Sync",id:"setting-up-rxdb--supabase-sync",level:2},{value:"Install Dependencies",id:"install-dependencies",level:3},{value:"Create a Supabase Project & Table",id:"create-a-supabase-project--table",level:3},{value:"Create an RxDB Database & Collection",id:"create-an-rxdb-database--collection",level:3},{value:"Create the Supabase Client",id:"create-the-supabase-client",level:3},{value:"Production",id:"production",level:4},{value:"Vite",id:"vite",level:4},{value:"Local Development",id:"local-development",level:4},{value:"Start Replication",id:"start-replication",level:3},{value:"Do other things with the replication state",id:"do-other-things-with-the-replication-state",level:3},{value:"Follow Up",id:"follow-up",level:2}];function k(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"supabase-replication-plugin-for-rxdb---real-time-offline-first-sync",children:"Supabase Replication Plugin for RxDB - Real-Time, Offline-First Sync"})}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/icons/supabase.svg",alt:"Supabase",height:"60",className:"img-padding img-in-text-right"})}),"\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.strong,{children:"Supabase Replication Plugin"})," for RxDB delivers seamless, two-way synchronization between your RxDB collections and a Supabase (Postgres) table. It uses ",(0,i.jsx)(s.strong,{children:"PostgREST"})," for pull/push and ",(0,i.jsx)(s.strong,{children:"Supabase Realtime"})," (logical replication) to stream live updates, so your data stays consistent across devices with first-class ",(0,i.jsx)(s.a,{href:"/articles/local-first-future.html",children:"local-first"}),", offline-ready support."]}),"\n",(0,i.jsxs)(s.p,{children:["Under the hood, the plugin is powered by the RxDB ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Sync Engine"}),". It handles checkpointed incremental pulls, robust retry logic, and ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"conflict detection/resolution"})," for you. You focus on features\u2014RxDB takes care of sync."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)(t.N,{videoId:"zBZgdTb-dns",title:"Supabase in 100 Seconds",duration:"2:36"})}),"\n",(0,i.jsx)(s.h2,{id:"key-features-of-the-rxdb-supabase-plugin",children:"Key Features of the RxDB-Supabase Plugin"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Cloud Only Backend"}),": No self-hosted server required. Client devices directly sync with the Supabase Servers."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Two-way replication"})," between Supabase tables and RxDB ",(0,i.jsx)(s.a,{href:"/rx-collection.html",children:"collections"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline-first"})," with resumable, incremental sync"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Live updates"})," via Supabase Realtime channels"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Conflict resolution"})," handled by the ",(0,i.jsx)(s.a,{href:"/replication.html",children:"RxDB Sync Engine"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Works in browsers and Node.js"})," with ",(0,i.jsx)(s.code,{children:"@supabase/supabase-js"})]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"architecture-overview",children:"Architecture Overview"}),"\n",(0,i.jsx)(c.u,{showServer:!1,dbIcon:"/files/icons/supabase.svg",dbLabel:"Supabase"}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsxs)(s.p,{children:["Clients connect ",(0,i.jsx)(s.strong,{children:"directly to Supabase"})," using the official JS client. The plugin:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Pulls"})," documents over PostgREST using a checkpoint ",(0,i.jsx)(s.code,{children:"(modified, id)"})," and deterministic ordering."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Pushes"})," inserts/updates using optimistic concurrency guards."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Streams"})," new changes using Supabase Realtime so live replication stays up to date."]}),"\n"]}),"\n",(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["Because Supabase exposes Postgres over ",(0,i.jsx)(s.strong,{children:"HTTP/WebSocket"}),", you can safely replicate from browsers and mobile apps. Protect your data with ",(0,i.jsx)(s.strong,{children:"Row Level Security (RLS)"})," policies; use the ",(0,i.jsx)(s.strong,{children:"anon"})," key on clients and the ",(0,i.jsx)(s.strong,{children:"service role"})," key only on trusted servers."]})}),"\n",(0,i.jsx)(s.h2,{id:"setting-up-rxdb--supabase-sync",children:"Setting up RxDB \u2194 Supabase Sync"}),"\n",(0,i.jsxs)(l.g,{children:[(0,i.jsx)(s.h3,{id:"install-dependencies",children:"Install Dependencies"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" @supabase/supabase-js"})]})})})}),(0,i.jsx)(s.h3,{id:"create-a-supabase-project--table",children:"Create a Supabase Project & Table"}),(0,i.jsx)(s.p,{children:"In your supabase project, create a new table. Ensure that:"}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"The primary key must have the type text (Primary keys must always be strings in RxDB)"}),"\n",(0,i.jsxs)(s.li,{children:["You have an modified field which stores the last modification timestamp of a row (default is ",(0,i.jsx)(s.code,{children:"_modified"}),")"]}),"\n",(0,i.jsxs)(s.li,{children:['You have a boolean field which stores if a row should is "deleted". You should not hard-delete rows in Supabase, because clients would miss the deletion if they haven\'t been online at the deletion time. Instead, use a deleted ',(0,i.jsx)(s.code,{children:"boolean"})," to mark rows as deleted. This way all clients can still pull the deletion, and RxDB will hide the complexity on the client side."]}),"\n",(0,i.jsx)(s.li,{children:"Enable the realtime observation of writes to the table."}),"\n"]}),(0,i.jsx)(s.p,{children:'Here is an example for a "human" table:'}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"sql","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"sql","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"create"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" extension "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" not"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" exists"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" moddatetime "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" extensions;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"create"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" table"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:' "'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"public"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:'".'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"humans"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "passportId"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" primary key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "firstName"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" not null"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "lastName"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" not null"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "age"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" integer"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "_deleted"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" boolean"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" DEFAULT"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" false "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"NOT NULL"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "_modified"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" timestamp with time zone"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" DEFAULT"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" now"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"NOT NULL"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"-- auto-update the _modified timestamp"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"CREATE"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" TRIGGER"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" update_modified_datetime"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" BEFORE"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" UPDATE"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" public.humans "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"FOR"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" EACH "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"ROW"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"EXECUTE"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" FUNCTION"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" extensions.moddatetime("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'_modified'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"-- add a table to the publication so we can subscribe to changes"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"alter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" publication supabase_realtime "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"add"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" table"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "public"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"humans"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h3,{id:"create-an-rxdb-database--collection",children:"Create an RxDB Database & Collection"}),(0,i.jsxs)(s.p,{children:["Create a normal RxDB database, then add a collection whose ",(0,i.jsx)(s.strong,{children:"schema mirrors your Supabase table"}),". The ",(0,i.jsx)(s.strong,{children:"primary key must match"})," (same column name and type), and fields should be ",(0,i.jsx)(s.strong,{children:"top-level simple types"})," (string/number/boolean). You don\u2019t need to model server internals: the plugin maps the server\u2019s _deleted flag to doc._deleted automatically, and _modified is optional in your schema (the plugin strips it on push and will include it on pull only if you define it). For browsers use a persistent storage like Localstorage or IndexedDB. For tests you can use the ",(0,i.jsx)(s.a,{href:"/rx-storage-memory.html",children:"in-memory storage"}),"."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// client"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'passportId'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" passportId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'passportId'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'lastName'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"create-the-supabase-client",children:"Create the Supabase Client"}),(0,i.jsx)(s.p,{children:"Make a single Supabase client and reuse it across your app. In the browser, use the anon key (RLS-protected). On trusted servers you may use the service role key\u2014but never ship that to clients."}),(0,i.jsxs)(a.t,{children:[(0,i.jsx)(s.h4,{id:"production",children:"Production"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> client"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createClient } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@supabase/supabase-js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" supabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createClient"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'https://xyzcompany.supabase.co'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'eyJhbGciOi...'"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),(0,i.jsx)(s.h4,{id:"vite",children:"Vite"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> client"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createClient } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@supabase/supabase-js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" supabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createClient"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"meta"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"env"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"VITE_SUPABASE_URL"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // e.g. https://xyzcompany.supabase.co"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"meta"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"env"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"VITE_SUPABASE_ANON_KEY"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // anon key for browsers"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional options object here"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),(0,i.jsx)(s.h4,{id:"local-development",children:"Local Development"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> client"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createClient } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@supabase/supabase-js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" supabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createClient"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://127.0.0.1:54321'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})})]}),(0,i.jsx)(s.h3,{id:"start-replication",children:"Start Replication"}),(0,i.jsx)(s.p,{children:"Connect your RxDB collection to the Supabase table to start the replication."}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> client"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateSupabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-supabase'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replication"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateSupabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" tableName"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'humans'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" client"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" supabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'humans-supabase'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional: shape incoming docs"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" modifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (doc) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // map nullable age-field"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".age) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"delete"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".age;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" doc;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional: customize the pull query before fetching"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" queryBuilder: ({ query }) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Add filters, joins, or other PostgREST query modifiers"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // This runs before checkpoint filtering and ordering"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".eq"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"status"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "active"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional overrides if your column names differ:"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // modifiedField: '_modified',"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // deletedField: '_deleted'"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// (optional) observe errors and wait for the first sync barrier"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"replication"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"error$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(err "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".error"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'[replication]'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" err));"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replication"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".awaitInitialReplication"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),(0,i.jsx)(s.admonition,{title:"Nullable values must be mapped",type:"note",children:(0,i.jsxs)(s.p,{children:["Supabase returns ",(0,i.jsx)(s.code,{children:"null"})," for nullable columns, but in RxDB you often model those fields as optional (i.e., they can be undefined/missing). To avoid schema errors, map ",(0,i.jsx)(s.code,{children:"null"})," \u2192 ",(0,i.jsx)(s.code,{children:"undefined"})," in the ",(0,i.jsx)(s.code,{children:"pull.modifier"})," (usually by deleting the key)."]})}),(0,i.jsx)(s.h3,{id:"do-other-things-with-the-replication-state",children:"Do other things with the replication state"}),(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.code,{children:"RxSupabaseReplicationState"})," which is returned from ",(0,i.jsx)(s.code,{children:"replicateSupabase()"})," allows you to run all functionality of the normal ",(0,i.jsx)(s.a,{href:"/replication.html",children:"RxReplicationState"}),"."]})]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Replication API Reference:"})," Learn the core concepts and lifecycle hooks \u2014 ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Replication"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline-First Guide:"})," Caching, retries, and conflict strategies \u2014 ",(0,i.jsx)(s.a,{href:"/articles/local-first-future.html",children:"Local-First"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Supabase Essentials:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Row Level Security (RLS) \u2014 ",(0,i.jsx)(s.a,{href:"https://supabase.com/docs/guides/auth/row-level-security",children:"https://supabase.com/docs/guides/auth/row-level-security"})]}),"\n",(0,i.jsxs)(s.li,{children:["Realtime \u2014 ",(0,i.jsx)(s.a,{href:"https://supabase.com/docs/guides/realtime",children:"https://supabase.com/docs/guides/realtime"})]}),"\n",(0,i.jsxs)(s.li,{children:["Local dev with the Supabase CLI \u2014 ",(0,i.jsx)(s.a,{href:"https://supabase.com/docs/guides/cli",children:"https://supabase.com/docs/guides/cli"})]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Community:"})," Questions or feedback? Join our Discord \u2014 ",(0,i.jsx)(s.a,{href:"./chat",children:"Chat"})]}),"\n"]})]})}function j(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(k,{...e})}):k(e)}},7482(e,s,n){n.d(s,{u:()=>i});n(6540);var r=n(4848);function i({className:e="",style:s,clientLabels:n=["Client A","Client B","Client C"],serverLabel:i="RxServer",dbLabel:o="Backend",dbIcon:a="",showServer:l=!0}){const t="/files/logo/logo.svg",c=l?{}:{gridColumn:"2 / 5",width:"90%",marginLeft:"5%",marginRight:0};return(0,r.jsxs)("div",{className:`rxdb-diagram ${e}`,style:s,children:[(0,r.jsx)("style",{children:'\n .rxdb-diagram {\n padding-top: 20px;\n padding-bottom: 20px;\n box-sizing: border-box;\n color: inherit;\n width: 100%;\n max-width: 100%;\n overflow: hidden;\n margin: 0 auto;\n font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI",\n Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";\n }\n\n /* Stage keeps fixed aspect ratio and scales */\n .rxdb-diagram .stage {\n width: 100%;\n aspect-ratio: 816 / 336;\n position: relative;\n }\n\n /* Grid fills the stage completely */\n .rxdb-diagram .grid {\n position: absolute;\n inset: 0;\n display: grid;\n grid-template-columns:\n 26.960784% 5.882353% 31.862745% 5.882353% 29.411765%;\n grid-template-rows:\n 23.809524% 14.285714% 23.809524% 14.285714% 23.809524%;\n align-items: center;\n justify-items: stretch;\n }\n\n .rxdb-diagram {\n --stroke: clamp(1px, 0.35vmin, 2px);\n --radius: clamp(8px, 1.2vmin, 14px);\n --pad: clamp(8px, 1.2vmin, 18px);\n --line: currentColor;\n }\n\n .rxdb-diagram .logo {\n height: 1.2em;\n width: auto;\n display: inline-block;\n object-fit: contain;\n margin-right: 10px;\n }\n\n .rxdb-diagram .box {\n border: var(--stroke) dashed var(--line);\n border-radius: var(--radius);\n background: transparent;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n font-weight: 600;\n padding: var(--pad);\n line-height: 1.2;\n user-select: none;\n }\n\n /* Server and DB take full height of stage */\n .rxdb-diagram .server,\n .rxdb-diagram .db {\n grid-row: 1 / -1;\n height: 100%;\n }\n\n /* Horizontal arrows */\n .rxdb-diagram .arrow {\n position: relative;\n height: 0;\n border-top: var(--stroke) solid var(--line);\n width: calc(100% - 20%);\n margin-left: 10%;\n margin-right: -1.5%;\n }\n\n .rxdb-diagram .arrow::before,\n .rxdb-diagram .arrow::after {\n content: "";\n position: absolute;\n top: 50%;\n width: clamp(6px, 1.2vmin, 10px);\n height: clamp(6px, 1.2vmin, 10px);\n border-top: var(--stroke) solid var(--line);\n border-right: var(--stroke) solid var(--line);\n transform-origin: center;\n }\n .rxdb-diagram .arrow::before {\n left: 0;\n transform: translate(-0%, -60%) rotate(-135deg);\n }\n .rxdb-diagram .arrow::after {\n right: 0;\n transform: translate(0%, -60%) rotate(45deg);\n }\n '}),(0,r.jsx)("div",{className:"stage","aria-label":l?"Clients to RxServer to MongoDB diagram":"Clients to MongoDB diagram",children:(0,r.jsxs)("div",{className:"grid",children:[(0,r.jsxs)("div",{className:"box",style:{gridColumn:1,gridRow:1},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),n[0]||"Client A"]}),(0,r.jsx)("div",{className:"arrow",style:{gridColumn:l?2:c.gridColumn,gridRow:1,...l?{}:{width:"90%",marginLeft:"5%",marginRight:0}},"aria-hidden":!0}),l&&(0,r.jsxs)("div",{className:"box server",style:{gridColumn:3},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),i]}),l&&(0,r.jsx)("div",{className:"arrow",style:{gridColumn:4,gridRow:3},"aria-hidden":!0}),(0,r.jsxs)("div",{className:"box db",style:{gridColumn:5},children:[(0,r.jsx)("img",{src:a,alt:"",className:"logo","aria-hidden":!0}),o]}),(0,r.jsxs)("div",{className:"box",style:{gridColumn:1,gridRow:3},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),n[1]||"Client B"]}),(0,r.jsx)("div",{className:"arrow",style:{gridColumn:l?2:c.gridColumn,gridRow:3,...l?{}:{width:"90%",marginLeft:"5%",marginRight:0}},"aria-hidden":!0}),(0,r.jsxs)("div",{className:"box",style:{gridColumn:1,gridRow:5},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),n[2]||"Client C"]}),(0,r.jsx)("div",{className:"arrow",style:{gridColumn:l?2:c.gridColumn,gridRow:5,...l?{}:{width:"90%",marginLeft:"5%",marginRight:0}},"aria-hidden":!0})]})})]})}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/41f941a1.b03616ab.js b/docs/assets/js/41f941a1.b03616ab.js
deleted file mode 100644
index 5d7c3f4a8e2..00000000000
--- a/docs/assets/js/41f941a1.b03616ab.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5966],{2447(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"leader-election","title":"Leader Election","description":"RxDB comes with a leader-election which elects a leading instance between different instances in the same javascript runtime.","source":"@site/docs/leader-election.md","sourceDirName":".","slug":"/leader-election.html","permalink":"/leader-election.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Leader Election","slug":"leader-election.html"},"sidebar":"tutorialSidebar","previous":{"title":"Backup","permalink":"/backup.html"},"next":{"title":"Middleware","permalink":"/middleware.html"}}');var i=n(4848),a=n(8453);const o={title:"Leader Election",slug:"leader-election.html"},l="Leader-Election",t={},c=[{value:"Use-case-example",id:"use-case-example",level:2},{value:"Solution",id:"solution",level:2},{value:"Add the leader election plugin",id:"add-the-leader-election-plugin",level:2},{value:"Code-example",id:"code-example",level:2},{value:"Handle Duplicate Leaders",id:"handle-duplicate-leaders",level:2},{value:"Live-Example",id:"live-example",level:2},{value:"Try it out",id:"try-it-out",level:2},{value:"Notice",id:"notice",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",strong:"strong",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"leader-election",children:"Leader-Election"})}),"\n",(0,i.jsx)(s.p,{children:"RxDB comes with a leader-election which elects a leading instance between different instances in the same javascript runtime.\nBefore you read this, please check out on how many of your open browser-tabs you have opened the same website more than once. Count them, I will wait.."}),"\n",(0,i.jsx)(s.p,{children:"So if you would now inspect the traffic that these open tabs produce, you can see that many of them send exact the same data over wire for every tab. No matter if the data is sent with an open websocket or by polling."}),"\n",(0,i.jsx)(s.h2,{id:"use-case-example",children:"Use-case-example"}),"\n",(0,i.jsx)(s.p,{children:"Imagine we have a website which displays the current temperature of the visitors location in various charts, numbers or heatmaps. To always display the live-data, the website opens a websocket to our API-Server which sends the current temperature every 10 seconds. Using the way most sites are currently build, we can now open it in 5 browser-tabs and it will open 5 websockets which send data 6*5=30 times per minute. This will not only waste the power of your clients device, but also wastes your api-servers resources by opening redundant connections."}),"\n",(0,i.jsx)(s.h2,{id:"solution",children:"Solution"}),"\n",(0,i.jsxs)(s.p,{children:["The solution to this redundancy is the usage of a ",(0,i.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Leader_election",children:"leader-election"}),"-algorithm which makes sure that always exactly one tab is managing the remote-data-access. The managing tab is the elected leader and stays leader until it is closed. No matter how many tabs are opened or closed, there must be always exactly ",(0,i.jsx)(s.strong,{children:"one"})," leader.\nYou could now start implementing a messaging-system between your browser-tabs, hand out which one is leader, solve conflicts and reassign a new leader when the old one 'dies'.\nOr just use RxDB which does all these things for you."]}),"\n",(0,i.jsx)(s.h2,{id:"add-the-leader-election-plugin",children:"Add the leader election plugin"}),"\n",(0,i.jsxs)(s.p,{children:["To enable the leader election, you have to add the ",(0,i.jsx)(s.code,{children:"leader-election"})," plugin."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBLeaderElectionPlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/leader-election'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBLeaderElectionPlugin);"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"code-example",children:"Code-example"}),"\n",(0,i.jsx)(s.p,{children:"To make it easy, here is an example where the temperature is pulled every ten seconds and saved to a collection. The pulling starts at the moment where the opened tab becomes the leader."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'weatherDB'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myPassword'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" temperature"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".waitForLeadership"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .then"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Long lives the king!'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"); "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// <- runs when db becomes leader"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" setInterval"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" temp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'https://example.com/api/temp/'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"temperature"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" degrees"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" temp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" time"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getTime"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"handle-duplicate-leaders",children:"Handle Duplicate Leaders"}),"\n",(0,i.jsxs)(s.p,{children:["On rare occasions, it can happen that ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/broadcast-channel/blob/master/.github/README.md#handle-duplicate-leaders",children:"more than one leader"})," is elected. This can happen when the CPU is on 100% or for any other reason the JavaScript process is fully blocked for a long time.\nFor most cases this is not really problem because on duplicate leaders, both browser tabs replicate with the same backend anyways.\nTo handle the duplicate leader event, you can access the leader elector and set a handler:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getLeaderElectorByBroadcastChannel"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/leader-election'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" leaderElector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getLeaderElectorByBroadcastChannel"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(broadcastChannel);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"leaderElector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onduplicate"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Duplicate leader detected -> reload the page."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" location"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".reload"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"live-example",children:"Live-Example"}),"\n",(0,i.jsx)(s.p,{children:"In this example the leader is marked with the crown \u265b"}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/leader-election.gif",alt:"Leader Election",width:"300"})}),"\n",(0,i.jsx)(s.h2,{id:"try-it-out",children:"Try it out"}),"\n",(0,i.jsxs)(s.p,{children:["Run the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/angular",children:"angular-example"})," where the leading tab is marked with a crown on the top-right-corner."]}),"\n",(0,i.jsx)(s.h2,{id:"notice",children:"Notice"}),"\n",(0,i.jsxs)(s.p,{children:["The leader election is implemented via the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/broadcast-channel#using-the-leaderelection",children:"broadcast-channel module"}),".\nThe leader is elected between different processes on the same javascript-runtime. Like multiple tabs in the same browser or multiple NodeJs-processes on the same machine. It will not run between different replicated instances."]})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>o,x:()=>l});var r=n(6540);const i={},a=r.createContext(i);function o(e){const s=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),r.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4250.a201ce1c.js b/docs/assets/js/4250.a201ce1c.js
deleted file mode 100644
index d3ad9561d0f..00000000000
--- a/docs/assets/js/4250.a201ce1c.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! For license information please see 4250.a201ce1c.js.LICENSE.txt */
-(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4250],{392(e,t,n){"use strict";var i=n(6220),r=n(1622),s=n(6766);e.exports=function(e,t,n,o){var a=s(e.as._ua);if(a&&a[0]>=3&&a[1]>20&&((t=t||{}).additionalUA="autocomplete.js "+r),!n.source)return i.error("Missing 'source' key");var u=i.isFunction(n.source)?n.source:function(e){return e[n.source]};if(!n.index)return i.error("Missing 'index' key");var c=n.index;return o=o||{},function(a,l){e.search(a,t,function(e,a){if(e)i.error(e.message);else{if(a.hits.length>0){var h=a.hits[0],p=i.mixin({hitsPerPage:0},n);delete p.source,delete p.index;var f=s(c.as._ua);return f&&f[0]>=3&&f[1]>20&&(t.additionalUA="autocomplete.js "+r),void c.search(u(h),p,function(e,t){if(e)i.error(e.message);else{var n=[];if(o.includeAll){var r=o.allTitle||"All departments";n.push(i.mixin({facet:{value:r,count:t.nbHits}},i.cloneDeep(h)))}i.each(t.facets,function(e,t){i.each(e,function(e,r){n.push(i.mixin({facet:{facet:t,value:r,count:e}},i.cloneDeep(h)))})});for(var s=1;s1)for(var n=1;n=3&&n[1]>20&&((t=t||{}).additionalUA="autocomplete.js "+r),function(n,r){e.search(n,t,function(e,t){e?i.error(e.message):r(t.hits,t)})}}},1622(e){e.exports="0.37.0"},1805(e,t,n){"use strict";var i=n(874),r=/\s+/;function s(e,t,n,i){var s;if(!n)return this;for(t=t.split(r),n=i?function(e,t){return e.bind?e.bind(t):function(){e.apply(t,[].slice.call(arguments,0))}}(n,i):n,this._callbacks=this._callbacks||{};s=t.shift();)this._callbacks[s]=this._callbacks[s]||{sync:[],async:[]},this._callbacks[s][e].push(n);return this}function o(e,t,n){return function(){for(var i,r=0,s=e.length;!i&&r'),this.$menu.append(this.$empty),this.$empty.hide()),this.datasets=i.map(e.datasets,function(t){return function(e,t,n){return new u.Dataset(i.mixin({$menu:e,cssClasses:n},t))}(o.$menu,t,e.cssClasses)}),i.each(this.datasets,function(e){var t=e.getRoot();t&&0===t.parent().length&&o.$menu.append(t),e.onSync("rendered",o._onRendered,o)}),e.templates&&e.templates.footer&&(this.templates.footer=i.templatify(e.templates.footer),this.$menu.append(this.templates.footer()));var l=this;r.element(window).resize(function(){l._redraw()})}i.mixin(u.prototype,s,{_onSuggestionClick:function(e){this.trigger("suggestionClicked",r.element(e.currentTarget))},_onSuggestionMouseEnter:function(e){var t=r.element(e.currentTarget);if(!t.hasClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0))){this._removeCursor();var n=this;setTimeout(function(){n._setCursor(t,!1)},0)}},_onSuggestionMouseLeave:function(e){if(e.relatedTarget&&r.element(e.relatedTarget).closest("."+i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).length>0)return;this._removeCursor(),this.trigger("cursorRemoved")},_onRendered:function(e,t){if(this.isEmpty=i.every(this.datasets,function(e){return e.isEmpty()}),this.isEmpty)if(t.length>=this.minLength&&this.trigger("empty"),this.$empty)if(t.length=this.minLength?this._show():this._hide());this.trigger("datasetRendered")},_hide:function(){this.$container.hide()},_show:function(){this.$container.css("display","block"),this._redraw(),this.trigger("shown")},_redraw:function(){this.isOpen&&this.appendTo&&this.trigger("redrawn")},_getSuggestions:function(){return this.$menu.find(i.className(this.cssClasses.prefix,this.cssClasses.suggestion))},_getCursor:function(){return this.$menu.find(i.className(this.cssClasses.prefix,this.cssClasses.cursor)).first()},_setCursor:function(e,t){e.first().addClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).attr("aria-selected","true"),this.trigger("cursorMoved",t)},_removeCursor:function(){this._getCursor().removeClass(i.className(this.cssClasses.prefix,this.cssClasses.cursor,!0)).removeAttr("aria-selected")},_moveCursor:function(e){var t,n,i,r;this.isOpen&&(n=this._getCursor(),t=this._getSuggestions(),this._removeCursor(),-1!==(i=((i=t.index(n)+e)+1)%(t.length+1)-1)?(i<-1&&(i=t.length-1),this._setCursor(r=t.eq(i),!0),this._ensureVisible(r)):this.trigger("cursorRemoved"))},_ensureVisible:function(e){var t,n,i,r;n=(t=e.position().top)+e.height()+parseInt(e.css("margin-top"),10)+parseInt(e.css("margin-bottom"),10),i=this.$menu.scrollTop(),r=this.$menu.height()+parseInt(this.$menu.css("padding-top"),10)+parseInt(this.$menu.css("padding-bottom"),10),t<0?this.$menu.scrollTop(i+t):r]*>/,m=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,v=/^(?:body|html)$/i,x=/([A-Z])/g,b=["val","css","html","text","data","width","height","offset"],w=["after","prepend","before","append"],S=h.createElement("table"),C=h.createElement("tr"),E={tr:h.createElement("tbody"),tbody:S,thead:S,tfoot:S,td:C,th:C,"*":h.createElement("div")},T=/complete|loaded|interactive/,k=/^[\w-]*$/,_={},O=_.toString,A={},P=h.createElement("div"),L={tabindex:"tabIndex",readonly:"readOnly",for:"htmlFor",class:"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},Q=Array.isArray||function(e){return e instanceof Array};function I(e){return null==e?String(e):_[O.call(e)]||"object"}function N(e){return"function"==I(e)}function $(e){return null!=e&&e==e.window}function D(e){return null!=e&&e.nodeType==e.DOCUMENT_NODE}function R(e){return"object"==I(e)}function F(e){return R(e)&&!$(e)&&Object.getPrototypeOf(e)==Object.prototype}function j(e){var t=!!e&&"length"in e&&e.length,n=i.type(e);return"function"!=n&&!$(e)&&("array"==n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function M(e){return c.call(e,function(e){return null!=e})}function V(e){return e.length>0?i.fn.concat.apply([],e):e}function B(e){return e.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function q(e){return e in f?f[e]:f[e]=new RegExp("(^|\\s)"+e+"(\\s|$)")}function z(e,t){return"number"!=typeof t||d[B(e)]?t:t+"px"}function H(e){var t,n;return p[e]||(t=h.createElement(e),h.body.appendChild(t),n=getComputedStyle(t,"").getPropertyValue("display"),t.parentNode.removeChild(t),"none"==n&&(n="block"),p[e]=n),p[e]}function K(e){return"children"in e?l.call(e.children):i.map(e.childNodes,function(e){if(1==e.nodeType)return e})}function W(e,t){var n,i=e?e.length:0;for(n=0;n$2>")),n===t&&(n=g.test(e)&&RegExp.$1),n in E||(n="*"),(a=E[n]).innerHTML=""+e,s=i.each(l.call(a.childNodes),function(){a.removeChild(this)})),F(r)&&(o=i(s),i.each(r,function(e,t){b.indexOf(e)>-1?o[e](t):o.attr(e,t)})),s},A.Z=function(e,t){return new W(e,t)},A.isZ=function(e){return e instanceof A.Z},A.init=function(e,n){var r;if(!e)return A.Z();if("string"==typeof e)if("<"==(e=e.trim())[0]&&g.test(e))r=A.fragment(e,RegExp.$1,n),e=null;else{if(n!==t)return i(n).find(e);r=A.qsa(h,e)}else{if(N(e))return i(h).ready(e);if(A.isZ(e))return e;if(Q(e))r=M(e);else if(R(e))r=[e],e=null;else if(g.test(e))r=A.fragment(e.trim(),RegExp.$1,n),e=null;else{if(n!==t)return i(n).find(e);r=A.qsa(h,e)}}return A.Z(r,e)},(i=function(e,t){return A.init(e,t)}).extend=function(e){var t,n=l.call(arguments,1);return"boolean"==typeof e&&(t=e,e=n.shift()),n.forEach(function(n){U(e,n,t)}),e},A.qsa=function(e,t){var n,i="#"==t[0],r=!i&&"."==t[0],s=i||r?t.slice(1):t,o=k.test(s);return e.getElementById&&o&&i?(n=e.getElementById(s))?[n]:[]:1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType?[]:l.call(o&&!i&&e.getElementsByClassName?r?e.getElementsByClassName(s):e.getElementsByTagName(t):e.querySelectorAll(t))},i.contains=h.documentElement.contains?function(e,t){return e!==t&&e.contains(t)}:function(e,t){for(;t&&(t=t.parentNode);)if(t===e)return!0;return!1},i.type=I,i.isFunction=N,i.isWindow=$,i.isArray=Q,i.isPlainObject=F,i.isEmptyObject=function(e){var t;for(t in e)return!1;return!0},i.isNumeric=function(e){var t=Number(e),n=typeof e;return null!=e&&"boolean"!=n&&("string"!=n||e.length)&&!isNaN(t)&&isFinite(t)||!1},i.inArray=function(e,t,n){return a.indexOf.call(t,e,n)},i.camelCase=s,i.trim=function(e){return null==e?"":String.prototype.trim.call(e)},i.uuid=0,i.support={},i.expr={},i.noop=function(){},i.map=function(e,t){var n,i,r,s=[];if(j(e))for(i=0;i=0?e:e+this.length]},toArray:function(){return this.get()},size:function(){return this.length},remove:function(){return this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(e){return a.every.call(this,function(t,n){return!1!==e.call(t,n,t)}),this},filter:function(e){return N(e)?this.not(this.not(e)):i(c.call(this,function(t){return A.matches(t,e)}))},add:function(e,t){return i(o(this.concat(i(e,t))))},is:function(e){return this.length>0&&A.matches(this[0],e)},not:function(e){var n=[];if(N(e)&&e.call!==t)this.each(function(t){e.call(this,t)||n.push(this)});else{var r="string"==typeof e?this.filter(e):j(e)&&N(e.item)?l.call(e):i(e);this.forEach(function(e){r.indexOf(e)<0&&n.push(e)})}return i(n)},has:function(e){return this.filter(function(){return R(e)?i.contains(this,e):i(this).find(e).size()})},eq:function(e){return-1===e?this.slice(e):this.slice(e,+e+1)},first:function(){var e=this[0];return e&&!R(e)?e:i(e)},last:function(){var e=this[this.length-1];return e&&!R(e)?e:i(e)},find:function(e){var t=this;return e?"object"==typeof e?i(e).filter(function(){var e=this;return a.some.call(t,function(t){return i.contains(t,e)})}):1==this.length?i(A.qsa(this[0],e)):this.map(function(){return A.qsa(this,e)}):i()},closest:function(e,t){var n=[],r="object"==typeof e&&i(e);return this.each(function(i,s){for(;s&&!(r?r.indexOf(s)>=0:A.matches(s,e));)s=s!==t&&!D(s)&&s.parentNode;s&&n.indexOf(s)<0&&n.push(s)}),i(n)},parents:function(e){for(var t=[],n=this;n.length>0;)n=i.map(n,function(e){if((e=e.parentNode)&&!D(e)&&t.indexOf(e)<0)return t.push(e),e});return G(t,e)},parent:function(e){return G(o(this.pluck("parentNode")),e)},children:function(e){return G(this.map(function(){return K(this)}),e)},contents:function(){return this.map(function(){return this.contentDocument||l.call(this.childNodes)})},siblings:function(e){return G(this.map(function(e,t){return c.call(K(t.parentNode),function(e){return e!==t})}),e)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(e){return i.map(this,function(t){return t[e]})},show:function(){return this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=H(this.nodeName))})},replaceWith:function(e){return this.before(e).remove()},wrap:function(e){var t=N(e);if(this[0]&&!t)var n=i(e).get(0),r=n.parentNode||this.length>1;return this.each(function(s){i(this).wrapAll(t?e.call(this,s):r?n.cloneNode(!0):n)})},wrapAll:function(e){if(this[0]){var t;for(i(this[0]).before(e=i(e));(t=e.children()).length;)e=t.first();i(e).append(this)}return this},wrapInner:function(e){var t=N(e);return this.each(function(n){var r=i(this),s=r.contents(),o=t?e.call(this,n):e;s.length?s.wrapAll(o):r.append(o)})},unwrap:function(){return this.parent().each(function(){i(this).replaceWith(i(this).children())}),this},clone:function(){return this.map(function(){return this.cloneNode(!0)})},hide:function(){return this.css("display","none")},toggle:function(e){return this.each(function(){var n=i(this);(e===t?"none"==n.css("display"):e)?n.show():n.hide()})},prev:function(e){return i(this.pluck("previousElementSibling")).filter(e||"*")},next:function(e){return i(this.pluck("nextElementSibling")).filter(e||"*")},html:function(e){return 0 in arguments?this.each(function(t){var n=this.innerHTML;i(this).empty().append(Z(this,e,t,n))}):0 in this?this[0].innerHTML:null},text:function(e){return 0 in arguments?this.each(function(t){var n=Z(this,e,t,this.textContent);this.textContent=null==n?"":""+n}):0 in this?this.pluck("textContent").join(""):null},attr:function(e,i){var r;return"string"!=typeof e||1 in arguments?this.each(function(t){if(1===this.nodeType)if(R(e))for(n in e)X(this,n,e[n]);else X(this,e,Z(this,i,t,this.getAttribute(e)))}):0 in this&&1==this[0].nodeType&&null!=(r=this[0].getAttribute(e))?r:t},removeAttr:function(e){return this.each(function(){1===this.nodeType&&e.split(" ").forEach(function(e){X(this,e)},this)})},prop:function(e,t){return e=L[e]||e,1 in arguments?this.each(function(n){this[e]=Z(this,t,n,this[e])}):this[0]&&this[0][e]},removeProp:function(e){return e=L[e]||e,this.each(function(){delete this[e]})},data:function(e,n){var i="data-"+e.replace(x,"-$1").toLowerCase(),r=1 in arguments?this.attr(i,n):this.attr(i);return null!==r?Y(r):t},val:function(e){return 0 in arguments?(null==e&&(e=""),this.each(function(t){this.value=Z(this,e,t,this.value)})):this[0]&&(this[0].multiple?i(this[0]).find("option").filter(function(){return this.selected}).pluck("value"):this[0].value)},offset:function(t){if(t)return this.each(function(e){var n=i(this),r=Z(this,t,e,n.offset()),s=n.offsetParent().offset(),o={top:r.top-s.top,left:r.left-s.left};"static"==n.css("position")&&(o.position="relative"),n.css(o)});if(!this.length)return null;if(h.documentElement!==this[0]&&!i.contains(h.documentElement,this[0]))return{top:0,left:0};var n=this[0].getBoundingClientRect();return{left:n.left+e.pageXOffset,top:n.top+e.pageYOffset,width:Math.round(n.width),height:Math.round(n.height)}},css:function(e,t){if(arguments.length<2){var r=this[0];if("string"==typeof e){if(!r)return;return r.style[s(e)]||getComputedStyle(r,"").getPropertyValue(e)}if(Q(e)){if(!r)return;var o={},a=getComputedStyle(r,"");return i.each(e,function(e,t){o[t]=r.style[s(t)]||a.getPropertyValue(t)}),o}}var u="";if("string"==I(e))t||0===t?u=B(e)+":"+z(e,t):this.each(function(){this.style.removeProperty(B(e))});else for(n in e)e[n]||0===e[n]?u+=B(n)+":"+z(n,e[n])+";":this.each(function(){this.style.removeProperty(B(n))});return this.each(function(){this.style.cssText+=";"+u})},index:function(e){return e?this.indexOf(i(e)[0]):this.parent().children().indexOf(this[0])},hasClass:function(e){return!!e&&a.some.call(this,function(e){return this.test(J(e))},q(e))},addClass:function(e){return e?this.each(function(t){if("className"in this){r=[];var n=J(this);Z(this,e,t,n).split(/\s+/g).forEach(function(e){i(this).hasClass(e)||r.push(e)},this),r.length&&J(this,n+(n?" ":"")+r.join(" "))}}):this},removeClass:function(e){return this.each(function(n){if("className"in this){if(e===t)return J(this,"");r=J(this),Z(this,e,n,r).split(/\s+/g).forEach(function(e){r=r.replace(q(e)," ")}),J(this,r.trim())}})},toggleClass:function(e,n){return e?this.each(function(r){var s=i(this);Z(this,e,r,J(this)).split(/\s+/g).forEach(function(e){(n===t?!s.hasClass(e):n)?s.addClass(e):s.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var n="scrollTop"in this[0];return e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var n="scrollLeft"in this[0];return e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var e=this[0],t=this.offsetParent(),n=this.offset(),r=v.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(i(e).css("margin-top"))||0,n.left-=parseFloat(i(e).css("margin-left"))||0,r.top+=parseFloat(i(t[0]).css("border-top-width"))||0,r.left+=parseFloat(i(t[0]).css("border-left-width"))||0,{top:n.top-r.top,left:n.left-r.left}}},offsetParent:function(){return this.map(function(){for(var e=this.offsetParent||h.body;e&&!v.test(e.nodeName)&&"static"==i(e).css("position");)e=e.offsetParent;return e})}},i.fn.detach=i.fn.remove,["width","height"].forEach(function(e){var n=e.replace(/./,function(e){return e[0].toUpperCase()});i.fn[e]=function(r){var s,o=this[0];return r===t?$(o)?o["inner"+n]:D(o)?o.documentElement["scroll"+n]:(s=this.offset())&&s[e]:this.each(function(t){(o=i(this)).css(e,Z(this,r,t,o[e]()))})}}),w.forEach(function(n,r){var s=r%2;i.fn[n]=function(){var n,o,a=i.map(arguments,function(e){var r=[];return"array"==(n=I(e))?(e.forEach(function(e){return e.nodeType!==t?r.push(e):i.zepto.isZ(e)?r=r.concat(e.get()):void(r=r.concat(A.fragment(e)))}),r):"object"==n||null==e?e:A.fragment(e)}),u=this.length>1;return a.length<1?this:this.each(function(t,n){o=s?n:n.parentNode,n=0==r?n.nextSibling:1==r?n.firstChild:2==r?n:null;var c=i.contains(h.documentElement,o);a.forEach(function(t){if(u)t=t.cloneNode(!0);else if(!o)return i(t).remove();o.insertBefore(t,n),c&&ee(t,function(t){if(!(null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src)){var n=t.ownerDocument?t.ownerDocument.defaultView:e;n.eval.call(n,t.innerHTML)}})})})},i.fn[s?n+"To":"insert"+(r?"Before":"After")]=function(e){return i(e)[n](this),this}}),A.Z.prototype=W.prototype=i.fn,A.uniq=o,A.deserializeValue=Y,i.zepto=A,i}();return function(t){var n,i=1,r=Array.prototype.slice,s=t.isFunction,o=function(e){return"string"==typeof e},a={},u={},c="onfocusin"in e,l={focus:"focusin",blur:"focusout"},h={mouseenter:"mouseover",mouseleave:"mouseout"};function p(e){return e._zid||(e._zid=i++)}function f(e,t,n,i){if((t=d(t)).ns)var r=g(t.ns);return(a[p(e)]||[]).filter(function(e){return e&&(!t.e||e.e==t.e)&&(!t.ns||r.test(e.ns))&&(!n||p(e.fn)===p(n))&&(!i||e.sel==i)})}function d(e){var t=(""+e).split(".");return{e:t[0],ns:t.slice(1).sort().join(" ")}}function g(e){return new RegExp("(?:^| )"+e.replace(" "," .* ?")+"(?: |$)")}function m(e,t){return e.del&&!c&&e.e in l||!!t}function y(e){return h[e]||c&&l[e]||e}function v(e,i,r,s,o,u,c){var l=p(e),f=a[l]||(a[l]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return t(document).ready(r);var a=d(i);a.fn=r,a.sel=o,a.e in h&&(r=function(e){var n=e.relatedTarget;if(!n||n!==this&&!t.contains(this,n))return a.fn.apply(this,arguments)}),a.del=u;var l=u||r;a.proxy=function(t){if(!(t=E(t)).isImmediatePropagationStopped()){try{var i=Object.getOwnPropertyDescriptor(t,"data");i&&!i.writable||(t.data=s)}catch(t){}var r=l.apply(e,t._args==n?[t]:[t].concat(t._args));return!1===r&&(t.preventDefault(),t.stopPropagation()),r}},a.i=f.length,f.push(a),"addEventListener"in e&&e.addEventListener(y(a.e),a.proxy,m(a,c))})}function x(e,t,n,i,r){var s=p(e);(t||"").split(/\s/).forEach(function(t){f(e,t,n,i).forEach(function(t){delete a[s][t.i],"removeEventListener"in e&&e.removeEventListener(y(t.e),t.proxy,m(t,r))})})}u.click=u.mousedown=u.mouseup=u.mousemove="MouseEvents",t.event={add:v,remove:x},t.proxy=function(e,n){var i=2 in arguments&&r.call(arguments,2);if(s(e)){var a=function(){return e.apply(n,i?i.concat(r.call(arguments)):arguments)};return a._zid=p(e),a}if(o(n))return i?(i.unshift(e[n],e),t.proxy.apply(null,i)):t.proxy(e[n],e);throw new TypeError("expected function")},t.fn.bind=function(e,t,n){return this.on(e,t,n)},t.fn.unbind=function(e,t){return this.off(e,t)},t.fn.one=function(e,t,n,i){return this.on(e,t,n,i,1)};var b=function(){return!0},w=function(){return!1},S=/^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,C={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};function E(e,i){return!i&&e.isDefaultPrevented||(i||(i=e),t.each(C,function(t,n){var r=i[t];e[t]=function(){return this[n]=b,r&&r.apply(i,arguments)},e[n]=w}),e.timeStamp||(e.timeStamp=Date.now()),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in i?!1===i.returnValue:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=b)),e}function T(e){var t,i={originalEvent:e};for(t in e)S.test(t)||e[t]===n||(i[t]=e[t]);return E(i,e)}t.fn.delegate=function(e,t,n){return this.on(t,e,n)},t.fn.undelegate=function(e,t,n){return this.off(t,e,n)},t.fn.live=function(e,n){return t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,i,a,u,c){var l,h,p=this;return e&&!o(e)?(t.each(e,function(e,t){p.on(e,i,a,t,c)}),p):(o(i)||s(u)||!1===u||(u=a,a=i,i=n),u!==n&&!1!==a||(u=a,a=n),!1===u&&(u=w),p.each(function(n,s){c&&(l=function(e){return x(s,e.type,u),u.apply(this,arguments)}),i&&(h=function(e){var n,o=t(e.target).closest(i,s).get(0);if(o&&o!==s)return n=t.extend(T(e),{currentTarget:o,liveFired:s}),(l||u).apply(o,[n].concat(r.call(arguments,1)))}),v(s,e,u,a,i,h||l)}))},t.fn.off=function(e,i,r){var a=this;return e&&!o(e)?(t.each(e,function(e,t){a.off(e,i,t)}),a):(o(i)||s(r)||!1===r||(r=i,i=n),!1===r&&(r=w),a.each(function(){x(this,e,r,i)}))},t.fn.trigger=function(e,n){return(e=o(e)||t.isPlainObject(e)?t.Event(e):E(e))._args=n,this.each(function(){e.type in l&&"function"==typeof this[e.type]?this[e.type]():"dispatchEvent"in this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var i,r;return this.each(function(s,a){(i=T(o(e)?t.Event(e):e))._args=n,i.target=a,t.each(f(a,e.type||e),function(e,t){if(r=t.proxy(i),i.isImmediatePropagationStopped())return!1})}),r},"focusin focusout focus blur load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select keydown keypress keyup error".split(" ").forEach(function(e){t.fn[e]=function(t){return 0 in arguments?this.bind(e,t):this.trigger(e)}}),t.Event=function(e,t){o(e)||(e=(t=e).type);var n=document.createEvent(u[e]||"Events"),i=!0;if(t)for(var r in t)"bubbles"==r?i=!!t[r]:n[r]=t[r];return n.initEvent(e,i,!0),E(n)}}(i),n=[],i.fn.remove=function(){return this.each(function(){this.parentNode&&("IMG"===this.tagName&&(n.push(this),this.src="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",t&&clearTimeout(t),t=setTimeout(function(){n=[]},6e4)),this.parentNode.removeChild(this))})},function(e){var t={},n=e.fn.data,i=e.camelCase,r=e.expando="Zepto"+ +new Date,s=[];function o(s,o){var u=s[r],c=u&&t[u];if(void 0===o)return c||a(s);if(c){if(o in c)return c[o];var l=i(o);if(l in c)return c[l]}return n.call(e(s),o)}function a(n,s,o){var a=n[r]||(n[r]=++e.uuid),c=t[a]||(t[a]=u(n));return void 0!==s&&(c[i(s)]=o),c}function u(t){var n={};return e.each(t.attributes||s,function(t,r){0==r.name.indexOf("data-")&&(n[i(r.name.replace("data-",""))]=e.zepto.deserializeValue(r.value))}),n}e.fn.data=function(t,n){return void 0===n?e.isPlainObject(t)?this.each(function(n,i){e.each(t,function(e,t){a(i,e,t)})}):0 in this?o(this[0],t):void 0:this.each(function(){a(this,t,n)})},e.data=function(t,n,i){return e(t).data(n,i)},e.hasData=function(n){var i=n[r],s=i&&t[i];return!!s&&!e.isEmptyObject(s)},e.fn.removeData=function(n){return"string"==typeof n&&(n=n.split(/\s+/)),this.each(function(){var s=this[r],o=s&&t[s];o&&e.each(n||o,function(e){delete o[n?i(this):e]})})},["remove","empty"].forEach(function(t){var n=e.fn[t];e.fn[t]=function(){var e=this.find("*");return"remove"===t&&(e=e.add(this)),e.removeData(),n.call(this)}})}(i),i}(t)},3718(e){"use strict";e.exports={element:null}},4045(e,t,n){"use strict";var i=n(6220),r=n(3718);function s(e){e&&e.el||i.error("EventBus initialized without el"),this.$el=r.element(e.el)}i.mixin(s.prototype,{trigger:function(e,t,n,r){var s=i.Event("autocomplete:"+e);return this.$el.trigger(s,[t,n,r]),s}}),e.exports=s},4498(e,t,n){"use strict";e.exports=n(5275)},4499(e){"use strict";e.exports={wrapper:'',dropdown:'',dataset:'',suggestions:'',suggestion:''}},4710(e,t,n){"use strict";e.exports={hits:n(1242),popularIn:n(392)}},4714(e,t,n){var i=n(9110);i.Template=n(9549).Template,i.template=i.Template,e.exports=i},5275(e,t,n){"use strict";var i=n(3704);n(3718).element=i;var r=n(6220);r.isArray=i.isArray,r.isFunction=i.isFunction,r.isObject=i.isPlainObject,r.bind=i.proxy,r.each=function(e,t){i.each(e,function(e,n){return t(n,e)})},r.map=i.map,r.mixin=i.extend,r.Event=i.Event;var s="aaAutocomplete",o=n(8693),a=n(4045);function u(e,t,n,u){n=r.isArray(n)?n:[].slice.call(arguments,2);var c=i(e).each(function(e,r){var c=i(r),l=new a({el:c}),h=u||new o({input:c,eventBus:l,dropdownMenuContainer:t.dropdownMenuContainer,hint:void 0===t.hint||!!t.hint,minLength:t.minLength,autoselect:t.autoselect,autoselectOnBlur:t.autoselectOnBlur,tabAutocomplete:t.tabAutocomplete,openOnFocus:t.openOnFocus,templates:t.templates,debug:t.debug,clearOnSelected:t.clearOnSelected,cssClasses:t.cssClasses,datasets:n,keyboardShortcuts:t.keyboardShortcuts,appendTo:t.appendTo,autoWidth:t.autoWidth,ariaLabel:t.ariaLabel||r.getAttribute("aria-label")});c.data(s,h)});return c.autocomplete={},r.each(["open","close","getVal","setVal","destroy","getWrapper"],function(e){c.autocomplete[e]=function(){var t,n=arguments;return c.each(function(r,o){var a=i(o).data(s);t=a[e].apply(a,n)}),t}}),c}u.sources=o.sources,u.escapeHighlightedString=r.escapeHighlightedString;var c="autocomplete"in window,l=window.autocomplete;u.noConflict=function(){return c?window.autocomplete=l:delete window.autocomplete,u},e.exports=u},5723(e,t){"use strict";t.test=function(){return"document"in globalThis&&"onreadystatechange"in globalThis.document.createElement("script")},t.install=function(e){return function(){var t=globalThis.document.createElement("script");return t.onreadystatechange=function(){e(),t.onreadystatechange=null,t.parentNode.removeChild(t),t=null},globalThis.document.documentElement.appendChild(t),e}}},6220(e,t,n){"use strict";var i,r=n(3718);function s(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}e.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(e){if(void 0===e&&(e=navigator.userAgent),/(msie|trident)/i.test(e)){var t=e.match(/(msie |rv:)(\d+(.\d+)?)/i);if(t)return t[2]}return!1},escapeRegExChars:function(e){return e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(e){return"number"==typeof e},toStr:function(e){return null==e?"":e+""},cloneDeep:function(e){var t=this.mixin({},e),n=this;return this.each(t,function(e,i){e&&(n.isArray(e)?t[i]=[].concat(e):n.isObject(e)&&(t[i]=n.cloneDeep(e)))}),t},error:function(e){throw new Error(e)},every:function(e,t){var n=!0;return e?(this.each(e,function(i,r){n&&(n=t.call(null,i,r,e)&&n)}),!!n):n},any:function(e,t){var n=!1;return e?(this.each(e,function(i,r){if(t.call(null,i,r,e))return n=!0,!1}),n):n},getUniqueId:(i=0,function(){return i++}),templatify:function(e){if(this.isFunction(e))return e;var t=r.element(e);return"SCRIPT"===t.prop("tagName")?function(){return t.text()}:function(){return String(e)}},defer:function(e){setTimeout(e,0)},noop:function(){},formatPrefix:function(e,t){return t?"":e+"-"},className:function(e,t,n){return(n?"":".")+e+t},escapeHighlightedString:function(e,t,n){t=t||"";var i=document.createElement("div");i.appendChild(document.createTextNode(t)),n=n||"";var r=document.createElement("div");r.appendChild(document.createTextNode(n));var o=document.createElement("div");return o.appendChild(document.createTextNode(e)),o.innerHTML.replace(RegExp(s(i.innerHTML),"g"),t).replace(RegExp(s(r.innerHTML),"g"),n)}}},6345(e,t){"use strict";t.test=function(){return!0},t.install=function(e){return function(){setTimeout(e,0)}}},6486(e,t){"use strict";t.test=function(){return!globalThis.setImmediate&&void 0!==globalThis.MessageChannel},t.install=function(e){var t=new globalThis.MessageChannel;return t.port1.onmessage=e,function(){t.port2.postMessage(0)}}},6766(e){"use strict";e.exports=function(e){var t=e.match(/Algolia for vanilla JavaScript (\d+\.)(\d+\.)(\d+)/);if(t)return[t[1],t[2],t[3]]}},7748(e,t,n){"use strict";var i;i={9:"tab",27:"esc",37:"left",39:"right",13:"enter",38:"up",40:"down"};var r=n(6220),s=n(3718),o=n(1805);function a(e){var t,n,o,a,u,c=this;(e=e||{}).input||r.error("input is missing"),t=r.bind(this._onBlur,this),n=r.bind(this._onFocus,this),o=r.bind(this._onKeydown,this),a=r.bind(this._onInput,this),this.$hint=s.element(e.hint),this.$input=s.element(e.input).on("blur.aa",t).on("focus.aa",n).on("keydown.aa",o),0===this.$hint.length&&(this.setHint=this.getHint=this.clearHint=this.clearHintIfInvalid=r.noop),r.isMsie()?this.$input.on("keydown.aa keypress.aa cut.aa paste.aa",function(e){i[e.which||e.keyCode]||r.defer(r.bind(c._onInput,c,e))}):this.$input.on("input.aa",a),this.query=this.$input.val(),this.$overflowHelper=(u=this.$input,s.element('').css({position:"absolute",visibility:"hidden",whiteSpace:"pre",fontFamily:u.css("font-family"),fontSize:u.css("font-size"),fontStyle:u.css("font-style"),fontVariant:u.css("font-variant"),fontWeight:u.css("font-weight"),wordSpacing:u.css("word-spacing"),letterSpacing:u.css("letter-spacing"),textIndent:u.css("text-indent"),textRendering:u.css("text-rendering"),textTransform:u.css("text-transform")}).insertAfter(u))}function u(e){return e.altKey||e.ctrlKey||e.metaKey||e.shiftKey}a.normalizeQuery=function(e){return(e||"").replace(/^\s*/g,"").replace(/\s{2,}/g," ")},r.mixin(a.prototype,o,{_onBlur:function(){this.resetInputValue(),this.$input.removeAttr("aria-activedescendant"),this.trigger("blurred")},_onFocus:function(){this.trigger("focused")},_onKeydown:function(e){var t=i[e.which||e.keyCode];this._managePreventDefault(t,e),t&&this._shouldTrigger(t,e)&&this.trigger(t+"Keyed",e)},_onInput:function(){this._checkInputValue()},_managePreventDefault:function(e,t){var n,i,r;switch(e){case"tab":i=this.getHint(),r=this.getInputValue(),n=i&&i!==r&&!u(t);break;case"up":case"down":n=!u(t);break;default:n=!1}n&&t.preventDefault()},_shouldTrigger:function(e,t){var n;if("tab"===e)n=!u(t);else n=!0;return n},_checkInputValue:function(){var e,t,n,i,r;e=this.getInputValue(),i=e,r=this.query,n=!(!(t=a.normalizeQuery(i)===a.normalizeQuery(r))||!this.query)&&this.query.length!==e.length,this.query=e,t?n&&this.trigger("whitespaceChanged",this.query):this.trigger("queryChanged",this.query)},focus:function(){this.$input.focus()},blur:function(){this.$input.blur()},getQuery:function(){return this.query},setQuery:function(e){this.query=e},getInputValue:function(){return this.$input.val()},setInputValue:function(e,t){void 0===e&&(e=this.query),this.$input.val(e),t?this.clearHint():this._checkInputValue()},expand:function(){this.$input.attr("aria-expanded","true")},collapse:function(){this.$input.attr("aria-expanded","false")},setActiveDescendant:function(e){this.$input.attr("aria-activedescendant",e)},removeActiveDescendant:function(){this.$input.removeAttr("aria-activedescendant")},resetInputValue:function(){this.setInputValue(this.query,!0)},getHint:function(){return this.$hint.val()},setHint:function(e){this.$hint.val(e)},clearHint:function(){this.setHint("")},clearHintIfInvalid:function(){var e,t,n;n=(e=this.getInputValue())!==(t=this.getHint())&&0===t.indexOf(e),""!==e&&n&&!this.hasOverflow()||this.clearHint()},getLanguageDirection:function(){return(this.$input.css("direction")||"ltr").toLowerCase()},hasOverflow:function(){var e=this.$input.width()-2;return this.$overflowHelper.text(this.getInputValue()),this.$overflowHelper.width()>=e},isCursorAtEnd:function(){var e,t,n;return e=this.$input.val().length,t=this.$input[0].selectionStart,r.isNumber(t)?t===e:!document.selection||((n=document.selection.createRange()).moveStart("character",-e),e===n.text.length)},destroy:function(){this.$hint.off(".aa"),this.$input.off(".aa"),this.$hint=this.$input=this.$overflowHelper=null}}),e.exports=a},8291(e,t,n){var i,r;!function(){var s,o,a,u,c,l,h,p,f,d,g,m,y,v,x,b,w,S,C,E,T,k,_,O,A,P,L,Q,I,N,$=function(e){var t=new $.Builder;return t.pipeline.add($.trimmer,$.stopWordFilter,$.stemmer),t.searchPipeline.add($.stemmer),e.call(t,t),t.build()};$.version="2.3.9",$.utils={},$.utils.warn=(s=this,function(e){s.console&&console.warn&&console.warn(e)}),$.utils.asString=function(e){return null==e?"":e.toString()},$.utils.clone=function(e){if(null==e)return e;for(var t=Object.create(null),n=Object.keys(e),i=0;i0){var u=$.utils.clone(t)||{};u.position=[o,a],u.index=r.length,r.push(new $.Token(n.slice(o,s),u))}o=s+1}}return r},$.tokenizer.separator=/[\s\-]+/,$.Pipeline=function(){this._stack=[]},$.Pipeline.registeredFunctions=Object.create(null),$.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&$.utils.warn("Overwriting existing registered function: "+t),e.label=t,$.Pipeline.registeredFunctions[e.label]=e},$.Pipeline.warnIfFunctionNotRegistered=function(e){e.label&&e.label in this.registeredFunctions||$.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},$.Pipeline.load=function(e){var t=new $.Pipeline;return e.forEach(function(e){var n=$.Pipeline.registeredFunctions[e];if(!n)throw new Error("Cannot load unregistered function: "+e);t.add(n)}),t},$.Pipeline.prototype.add=function(){Array.prototype.slice.call(arguments).forEach(function(e){$.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},$.Pipeline.prototype.after=function(e,t){$.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");n+=1,this._stack.splice(n,0,t)},$.Pipeline.prototype.before=function(e,t){$.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");this._stack.splice(n,0,t)},$.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);-1!=t&&this._stack.splice(t,1)},$.Pipeline.prototype.run=function(e){for(var t=this._stack.length,n=0;n1&&(se&&(n=r),s!=e);)i=n-t,r=t+Math.floor(i/2),s=this.elements[2*r];return s==e||s>e?2*r:sa?c+=2:o==a&&(t+=n[u+1]*i[c+1],u+=2,c+=2);return t},$.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},$.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,n=0;t0){var s,o=r.str.charAt(0);o in r.node.edges?s=r.node.edges[o]:(s=new $.TokenSet,r.node.edges[o]=s),1==r.str.length&&(s.final=!0),i.push({node:s,editsRemaining:r.editsRemaining,str:r.str.slice(1)})}if(0!=r.editsRemaining){if("*"in r.node.edges)var a=r.node.edges["*"];else{a=new $.TokenSet;r.node.edges["*"]=a}if(0==r.str.length&&(a.final=!0),i.push({node:a,editsRemaining:r.editsRemaining-1,str:r.str}),r.str.length>1&&i.push({node:r.node,editsRemaining:r.editsRemaining-1,str:r.str.slice(1)}),1==r.str.length&&(r.node.final=!0),r.str.length>=1){if("*"in r.node.edges)var u=r.node.edges["*"];else{u=new $.TokenSet;r.node.edges["*"]=u}1==r.str.length&&(u.final=!0),i.push({node:u,editsRemaining:r.editsRemaining-1,str:r.str.slice(1)})}if(r.str.length>1){var c,l=r.str.charAt(0),h=r.str.charAt(1);h in r.node.edges?c=r.node.edges[h]:(c=new $.TokenSet,r.node.edges[h]=c),1==r.str.length&&(c.final=!0),i.push({node:c,editsRemaining:r.editsRemaining-1,str:l+r.str.slice(2)})}}}return n},$.TokenSet.fromString=function(e){for(var t=new $.TokenSet,n=t,i=0,r=e.length;i=e;t--){var n=this.uncheckedNodes[t],i=n.child.toString();i in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[i]:(n.child._str=i,this.minimizedNodes[i]=n.child),this.uncheckedNodes.pop()}},$.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},$.Index.prototype.search=function(e){return this.query(function(t){new $.QueryParser(e,t).parse()})},$.Index.prototype.query=function(e){for(var t=new $.Query(this.fields),n=Object.create(null),i=Object.create(null),r=Object.create(null),s=Object.create(null),o=Object.create(null),a=0;a1?1:e},$.Builder.prototype.k1=function(e){this._k1=e},$.Builder.prototype.add=function(e,t){var n=e[this._ref],i=Object.keys(this._fields);this._documents[n]=t||{},this.documentCount+=1;for(var r=0;r=this.length)return $.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},$.QueryLexer.prototype.width=function(){return this.pos-this.start},$.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},$.QueryLexer.prototype.backup=function(){this.pos-=1},$.QueryLexer.prototype.acceptDigitRun=function(){var e,t;do{t=(e=this.next()).charCodeAt(0)}while(t>47&&t<58);e!=$.QueryLexer.EOS&&this.backup()},$.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit($.QueryLexer.TERM)),e.ignore(),e.more())return $.QueryLexer.lexText},$.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit($.QueryLexer.EDIT_DISTANCE),$.QueryLexer.lexText},$.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit($.QueryLexer.BOOST),$.QueryLexer.lexText},$.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit($.QueryLexer.TERM)},$.QueryLexer.termSeparator=$.tokenizer.separator,$.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==$.QueryLexer.EOS)return $.QueryLexer.lexEOS;if(92!=t.charCodeAt(0)){if(":"==t)return $.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit($.QueryLexer.TERM),$.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit($.QueryLexer.TERM),$.QueryLexer.lexBoost;if("+"==t&&1===e.width())return e.emit($.QueryLexer.PRESENCE),$.QueryLexer.lexText;if("-"==t&&1===e.width())return e.emit($.QueryLexer.PRESENCE),$.QueryLexer.lexText;if(t.match($.QueryLexer.termSeparator))return $.QueryLexer.lexTerm}else e.escapeCharacter()}},$.QueryParser=function(e,t){this.lexer=new $.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},$.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=$.QueryParser.parseClause;e;)e=e(this);return this.query},$.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},$.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},$.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},$.QueryParser.parseClause=function(e){var t=e.peekLexeme();if(null!=t)switch(t.type){case $.QueryLexer.PRESENCE:return $.QueryParser.parsePresence;case $.QueryLexer.FIELD:return $.QueryParser.parseField;case $.QueryLexer.TERM:return $.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+t.type;throw t.str.length>=1&&(n+=" with value '"+t.str+"'"),new $.QueryParseError(n,t.start,t.end)}},$.QueryParser.parsePresence=function(e){var t=e.consumeLexeme();if(null!=t){switch(t.str){case"-":e.currentClause.presence=$.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=$.Query.presence.REQUIRED;break;default:var n="unrecognised presence operator'"+t.str+"'";throw new $.QueryParseError(n,t.start,t.end)}var i=e.peekLexeme();if(null==i){n="expecting term or field, found nothing";throw new $.QueryParseError(n,t.start,t.end)}switch(i.type){case $.QueryLexer.FIELD:return $.QueryParser.parseField;case $.QueryLexer.TERM:return $.QueryParser.parseTerm;default:n="expecting term or field, found '"+i.type+"'";throw new $.QueryParseError(n,i.start,i.end)}}},$.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(null!=t){if(-1==e.query.allFields.indexOf(t.str)){var n=e.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),i="unrecognised field '"+t.str+"', possible fields: "+n;throw new $.QueryParseError(i,t.start,t.end)}e.currentClause.fields=[t.str];var r=e.peekLexeme();if(null==r){i="expecting term, found nothing";throw new $.QueryParseError(i,t.start,t.end)}if(r.type===$.QueryLexer.TERM)return $.QueryParser.parseTerm;i="expecting term, found '"+r.type+"'";throw new $.QueryParseError(i,r.start,r.end)}},$.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(null!=t){e.currentClause.term=t.str.toLowerCase(),-1!=t.str.indexOf("*")&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(null!=n)switch(n.type){case $.QueryLexer.TERM:return e.nextClause(),$.QueryParser.parseTerm;case $.QueryLexer.FIELD:return e.nextClause(),$.QueryParser.parseField;case $.QueryLexer.EDIT_DISTANCE:return $.QueryParser.parseEditDistance;case $.QueryLexer.BOOST:return $.QueryParser.parseBoost;case $.QueryLexer.PRESENCE:return e.nextClause(),$.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+n.type+"'";throw new $.QueryParseError(i,n.start,n.end)}else e.nextClause()}},$.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(null!=t){var n=parseInt(t.str,10);if(isNaN(n)){var i="edit distance must be numeric";throw new $.QueryParseError(i,t.start,t.end)}e.currentClause.editDistance=n;var r=e.peekLexeme();if(null!=r)switch(r.type){case $.QueryLexer.TERM:return e.nextClause(),$.QueryParser.parseTerm;case $.QueryLexer.FIELD:return e.nextClause(),$.QueryParser.parseField;case $.QueryLexer.EDIT_DISTANCE:return $.QueryParser.parseEditDistance;case $.QueryLexer.BOOST:return $.QueryParser.parseBoost;case $.QueryLexer.PRESENCE:return e.nextClause(),$.QueryParser.parsePresence;default:i="Unexpected lexeme type '"+r.type+"'";throw new $.QueryParseError(i,r.start,r.end)}else e.nextClause()}},$.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(null!=t){var n=parseInt(t.str,10);if(isNaN(n)){var i="boost must be numeric";throw new $.QueryParseError(i,t.start,t.end)}e.currentClause.boost=n;var r=e.peekLexeme();if(null!=r)switch(r.type){case $.QueryLexer.TERM:return e.nextClause(),$.QueryParser.parseTerm;case $.QueryLexer.FIELD:return e.nextClause(),$.QueryParser.parseField;case $.QueryLexer.EDIT_DISTANCE:return $.QueryParser.parseEditDistance;case $.QueryLexer.BOOST:return $.QueryParser.parseBoost;case $.QueryLexer.PRESENCE:return e.nextClause(),$.QueryParser.parsePresence;default:i="Unexpected lexeme type '"+r.type+"'";throw new $.QueryParseError(i,r.start,r.end)}else e.nextClause()}},void 0===(r="function"==typeof(i=function(){return $})?i.call(t,n,t,e):i)||(e.exports=r)}()},8693(e,t,n){"use strict";var i="aaAttrs",r=n(6220),s=n(3718),o=n(4045),a=n(7748),u=n(2731),c=n(4499),l=n(819);function h(e){var t,n;if((e=e||{}).input||r.error("missing input"),this.isActivated=!1,this.debug=!!e.debug,this.autoselect=!!e.autoselect,this.autoselectOnBlur=!!e.autoselectOnBlur,this.openOnFocus=!!e.openOnFocus,this.minLength=r.isNumber(e.minLength)?e.minLength:1,this.autoWidth=void 0===e.autoWidth||!!e.autoWidth,this.clearOnSelected=!!e.clearOnSelected,this.tabAutocomplete=void 0===e.tabAutocomplete||!!e.tabAutocomplete,e.hint=!!e.hint,e.hint&&e.appendTo)throw new Error("[autocomplete.js] hint and appendTo options can't be used at the same time");this.css=e.css=r.mixin({},l,e.appendTo?l.appendTo:{}),this.cssClasses=e.cssClasses=r.mixin({},l.defaultClasses,e.cssClasses||{}),this.cssClasses.prefix=e.cssClasses.formattedPrefix=r.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix),this.listboxId=e.listboxId=[this.cssClasses.root,"listbox",r.getUniqueId()].join("-");var a=function(e){var t,n,o,a;t=s.element(e.input),n=s.element(c.wrapper.replace("%ROOT%",e.cssClasses.root)).css(e.css.wrapper),e.appendTo||"block"!==t.css("display")||"table"!==t.parent().css("display")||n.css("display","table-cell");var u=c.dropdown.replace("%PREFIX%",e.cssClasses.prefix).replace("%DROPDOWN_MENU%",e.cssClasses.dropdownMenu);o=s.element(u).css(e.css.dropdown).attr({role:"listbox",id:e.listboxId}),e.templates&&e.templates.dropdownMenu&&o.html(r.templatify(e.templates.dropdownMenu)());a=t.clone().css(e.css.hint).css(function(e){return{backgroundAttachment:e.css("background-attachment"),backgroundClip:e.css("background-clip"),backgroundColor:e.css("background-color"),backgroundImage:e.css("background-image"),backgroundOrigin:e.css("background-origin"),backgroundPosition:e.css("background-position"),backgroundRepeat:e.css("background-repeat"),backgroundSize:e.css("background-size")}}(t)),a.val("").addClass(r.className(e.cssClasses.prefix,e.cssClasses.hint,!0)).removeAttr("id name placeholder required").prop("readonly",!0).attr({"aria-hidden":"true",autocomplete:"off",spellcheck:"false",tabindex:-1}),a.removeData&&a.removeData();t.data(i,{"aria-autocomplete":t.attr("aria-autocomplete"),"aria-expanded":t.attr("aria-expanded"),"aria-owns":t.attr("aria-owns"),autocomplete:t.attr("autocomplete"),dir:t.attr("dir"),role:t.attr("role"),spellcheck:t.attr("spellcheck"),style:t.attr("style"),type:t.attr("type")}),t.addClass(r.className(e.cssClasses.prefix,e.cssClasses.input,!0)).attr({autocomplete:"off",spellcheck:!1,role:"combobox","aria-autocomplete":e.datasets&&e.datasets[0]&&e.datasets[0].displayKey?"both":"list","aria-expanded":"false","aria-label":e.ariaLabel,"aria-owns":e.listboxId}).css(e.hint?e.css.input:e.css.inputWithNoHint);try{t.attr("dir")||t.attr("dir","auto")}catch(l){}return n=e.appendTo?n.appendTo(s.element(e.appendTo).eq(0)).eq(0):t.wrap(n).parent(),n.prepend(e.hint?a:null).append(o),{wrapper:n,input:t,hint:a,menu:o}}(e);this.$node=a.wrapper;var u=this.$input=a.input;t=a.menu,n=a.hint,e.dropdownMenuContainer&&s.element(e.dropdownMenuContainer).css("position","relative").append(t.css("top","0")),u.on("blur.aa",function(e){var n=document.activeElement;r.isMsie()&&(t[0]===n||t[0].contains(n))&&(e.preventDefault(),e.stopImmediatePropagation(),r.defer(function(){u.focus()}))}),t.on("mousedown.aa",function(e){e.preventDefault()}),this.eventBus=e.eventBus||new o({el:u}),this.dropdown=new h.Dropdown({appendTo:e.appendTo,wrapper:this.$node,menu:t,datasets:e.datasets,templates:e.templates,cssClasses:e.cssClasses,minLength:this.minLength}).onSync("suggestionClicked",this._onSuggestionClicked,this).onSync("cursorMoved",this._onCursorMoved,this).onSync("cursorRemoved",this._onCursorRemoved,this).onSync("opened",this._onOpened,this).onSync("closed",this._onClosed,this).onSync("shown",this._onShown,this).onSync("empty",this._onEmpty,this).onSync("redrawn",this._onRedrawn,this).onAsync("datasetRendered",this._onDatasetRendered,this),this.input=new h.Input({input:u,hint:n}).onSync("focused",this._onFocused,this).onSync("blurred",this._onBlurred,this).onSync("enterKeyed",this._onEnterKeyed,this).onSync("tabKeyed",this._onTabKeyed,this).onSync("escKeyed",this._onEscKeyed,this).onSync("upKeyed",this._onUpKeyed,this).onSync("downKeyed",this._onDownKeyed,this).onSync("leftKeyed",this._onLeftKeyed,this).onSync("rightKeyed",this._onRightKeyed,this).onSync("queryChanged",this._onQueryChanged,this).onSync("whitespaceChanged",this._onWhitespaceChanged,this),this._bindKeyboardShortcuts(e),this._setLanguageDirection()}r.mixin(h.prototype,{_bindKeyboardShortcuts:function(e){if(e.keyboardShortcuts){var t=this.$input,n=[];r.each(e.keyboardShortcuts,function(e){"string"==typeof e&&(e=e.toUpperCase().charCodeAt(0)),n.push(e)}),s.element(document).keydown(function(e){var i=e.target||e.srcElement,r=i.tagName;if(!i.isContentEditable&&"INPUT"!==r&&"SELECT"!==r&&"TEXTAREA"!==r){var s=e.which||e.keyCode;-1!==n.indexOf(s)&&(t.focus(),e.stopPropagation(),e.preventDefault())}})}},_onSuggestionClicked:function(e,t){var n;(n=this.dropdown.getDatumForSuggestion(t))&&this._select(n,{selectionMethod:"click"})},_onCursorMoved:function(e,t){var n=this.dropdown.getDatumForCursor(),i=this.dropdown.getCurrentCursor().attr("id");this.input.setActiveDescendant(i),n&&(t&&this.input.setInputValue(n.value,!0),this.eventBus.trigger("cursorchanged",n.raw,n.datasetName))},_onCursorRemoved:function(){this.input.resetInputValue(),this._updateHint(),this.eventBus.trigger("cursorremoved")},_onDatasetRendered:function(){this._updateHint(),this.eventBus.trigger("updated")},_onOpened:function(){this._updateHint(),this.input.expand(),this.eventBus.trigger("opened")},_onEmpty:function(){this.eventBus.trigger("empty")},_onRedrawn:function(){this.$node.css("top","0px"),this.$node.css("left","0px");var e=this.$input[0].getBoundingClientRect();this.autoWidth&&this.$node.css("width",e.width+"px");var t=this.$node[0].getBoundingClientRect(),n=e.bottom-t.top;this.$node.css("top",n+"px");var i=e.left-t.left;this.$node.css("left",i+"px"),this.eventBus.trigger("redrawn")},_onShown:function(){this.eventBus.trigger("shown"),this.autoselect&&this.dropdown.cursorTopSuggestion()},_onClosed:function(){this.input.clearHint(),this.input.removeActiveDescendant(),this.input.collapse(),this.eventBus.trigger("closed")},_onFocused:function(){if(this.isActivated=!0,this.openOnFocus){var e=this.input.getQuery();e.length>=this.minLength?this.dropdown.update(e):this.dropdown.empty(),this.dropdown.open()}},_onBlurred:function(){var e,t;e=this.dropdown.getDatumForCursor(),t=this.dropdown.getDatumForTopSuggestion();var n={selectionMethod:"blur"};this.debug||(this.autoselectOnBlur&&e?this._select(e,n):this.autoselectOnBlur&&t?this._select(t,n):(this.isActivated=!1,this.dropdown.empty(),this.dropdown.close()))},_onEnterKeyed:function(e,t){var n,i;n=this.dropdown.getDatumForCursor(),i=this.dropdown.getDatumForTopSuggestion();var r={selectionMethod:"enterKey"};n?(this._select(n,r),t.preventDefault()):this.autoselect&&i&&(this._select(i,r),t.preventDefault())},_onTabKeyed:function(e,t){if(this.tabAutocomplete){var n;(n=this.dropdown.getDatumForCursor())?(this._select(n,{selectionMethod:"tabKey"}),t.preventDefault()):this._autocomplete(!0)}else this.dropdown.close()},_onEscKeyed:function(){this.dropdown.close(),this.input.resetInputValue()},_onUpKeyed:function(){var e=this.input.getQuery();this.dropdown.isEmpty&&e.length>=this.minLength?this.dropdown.update(e):this.dropdown.moveCursorUp(),this.dropdown.open()},_onDownKeyed:function(){var e=this.input.getQuery();this.dropdown.isEmpty&&e.length>=this.minLength?this.dropdown.update(e):this.dropdown.moveCursorDown(),this.dropdown.open()},_onLeftKeyed:function(){"rtl"===this.dir&&this._autocomplete()},_onRightKeyed:function(){"ltr"===this.dir&&this._autocomplete()},_onQueryChanged:function(e,t){this.input.clearHintIfInvalid(),t.length>=this.minLength?this.dropdown.update(t):this.dropdown.empty(),this.dropdown.open(),this._setLanguageDirection()},_onWhitespaceChanged:function(){this._updateHint(),this.dropdown.open()},_setLanguageDirection:function(){var e=this.input.getLanguageDirection();this.dir!==e&&(this.dir=e,this.$node.css("direction",e),this.dropdown.setLanguageDirection(e))},_updateHint:function(){var e,t,n,i,s;(e=this.dropdown.getDatumForTopSuggestion())&&this.dropdown.isVisible()&&!this.input.hasOverflow()?(t=this.input.getInputValue(),n=a.normalizeQuery(t),i=r.escapeRegExChars(n),(s=new RegExp("^(?:"+i+")(.+$)","i").exec(e.value))?this.input.setHint(t+s[1]):this.input.clearHint()):this.input.clearHint()},_autocomplete:function(e){var t,n,i,r;t=this.input.getHint(),n=this.input.getQuery(),i=e||this.input.isCursorAtEnd(),t&&n!==t&&i&&((r=this.dropdown.getDatumForTopSuggestion())&&this.input.setInputValue(r.value),this.eventBus.trigger("autocompleted",r.raw,r.datasetName))},_select:function(e,t){void 0!==e.value&&this.input.setQuery(e.value),this.clearOnSelected?this.setVal(""):this.input.setInputValue(e.value,!0),this._setLanguageDirection(),!1===this.eventBus.trigger("selected",e.raw,e.datasetName,t).isDefaultPrevented()&&(this.dropdown.close(),r.defer(r.bind(this.dropdown.empty,this.dropdown)))},open:function(){if(!this.isActivated){var e=this.input.getInputValue();e.length>=this.minLength?this.dropdown.update(e):this.dropdown.empty()}this.dropdown.open()},close:function(){this.dropdown.close()},setVal:function(e){e=r.toStr(e),this.isActivated?this.input.setInputValue(e):(this.input.setQuery(e),this.input.setInputValue(e,!0)),this._setLanguageDirection()},getVal:function(){return this.input.getQuery()},destroy:function(){this.input.destroy(),this.dropdown.destroy(),function(e,t){var n=e.find(r.className(t.prefix,t.input));r.each(n.data(i),function(e,t){void 0===e?n.removeAttr(t):n.attr(t,e)}),n.detach().removeClass(r.className(t.prefix,t.input,!0)).insertAfter(e),n.removeData&&n.removeData(i);e.remove()}(this.$node,this.cssClasses),this.$node=null},getWrapper:function(){return this.dropdown.$container[0]}}),h.Dropdown=u,h.Input=a,h.sources=n(4710),e.exports=h},9110(e,t){!function(e){var t=/\S/,n=/\"/g,i=/\n/g,r=/\r/g,s=/\\/g,o=/\u2028/,a=/\u2029/;function u(e){"}"===e.n.substr(e.n.length-1)&&(e.n=e.n.substring(0,e.n.length-1))}function c(e){return e.trim?e.trim():e.replace(/^\s*|\s*$/g,"")}function l(e,t,n){if(t.charAt(n)!=e.charAt(0))return!1;for(var i=1,r=e.length;i":7,"=":8,_v:9,"{":10,"&":11,_t:12},e.scan=function(n,i){var r=n.length,s=0,o=null,a=null,h="",p=[],f=!1,d=0,g=0,m="{{",y="}}";function v(){h.length>0&&(p.push({tag:"_t",text:new String(h)}),h="")}function x(n,i){if(v(),n&&function(){for(var n=!0,i=g;i"==r.tag&&(r.indent=p[s].text.toString()),p.splice(s,1));else i||p.push({tag:"\n"});f=!1,g=p.length}function b(e,t){var n="="+y,i=e.indexOf(n,t),r=c(e.substring(e.indexOf("=",t)+1,i)).split(" ");return m=r[0],y=r[r.length-1],i+n.length-1}for(i&&(i=i.split(" "),m=i[0],y=i[1]),d=0;d0;){if(u=t.shift(),s&&"<"==s.tag&&!(u.tag in h))throw new Error("Illegal content in < super tag.");if(e.tags[u.tag]<=e.tags.$||f(u,r))i.push(u),u.nodes=p(t,u.tag,i,r);else{if("/"==u.tag){if(0===i.length)throw new Error("Closing tag without opener: /"+u.n);if(a=i.pop(),u.n!=a.n&&!d(u.n,a.n,r))throw new Error("Nesting error: "+a.n+" vs. "+u.n);return a.end=u.i,o}"\n"==u.tag&&(u.last=0==t.length||"\n"==t[0].tag)}o.push(u)}if(i.length>0)throw new Error("missing closing tag: "+i.pop().n);return o}function f(e,t){for(var n=0,i=t.length;n":x,"<":function(t,n){var i={partials:{},code:"",subs:{},inPartial:!0};e.walk(t.nodes,i);var r=n.partials[x(t,n)];r.subs=i.subs,r.partials=i.partials},$:function(t,n){var i={subs:{},code:"",partials:n.partials,prefix:t.n};e.walk(t.nodes,i),n.subs[t.n]=i.code,n.inPartial||(n.code+='t.sub("'+y(t.n)+'",c,p,i);')},"\n":function(e,t){t.code+=w('"\\n"'+(e.last?"":" + i"))},_v:function(e,t){t.code+="t.b(t.v(t."+v(e.n)+'("'+y(e.n)+'",c,p,0)));'},_t:function(e,t){t.code+=w('"'+y(e.text)+'"')},"{":b,"&":b},e.walk=function(t,n){for(var i,r=0,s=t.length;r"+t(e)+"
"}}(e.templates,this.displayFn),this.css=o.mixin({},c,e.appendTo?c.appendTo:{}),this.cssClasses=e.cssClasses=o.mixin({},c.defaultClasses,e.cssClasses||{}),this.cssClasses.prefix=e.cssClasses.formattedPrefix||o.formatPrefix(this.cssClasses.prefix,this.cssClasses.noPrefix);var n=o.className(this.cssClasses.prefix,this.cssClasses.dataset);this.$el=e.$menu&&e.$menu.find(n+"-"+this.name).length>0?a.element(e.$menu.find(n+"-"+this.name)[0]):a.element(u.dataset.replace("%CLASS%",this.name).replace("%PREFIX%",this.cssClasses.prefix).replace("%DATASET%",this.cssClasses.dataset)),this.$menu=e.$menu,this.clearCachedSuggestions()}h.extractDatasetName=function(e){return a.element(e).data(i)},h.extractValue=function(e){return a.element(e).data(r)},h.extractDatum=function(e){var t=a.element(e).data(s);return"string"==typeof t&&(t=JSON.parse(t)),t},o.mixin(h.prototype,l,{_render:function(e,t){if(this.$el){var n,c=this,l=[].slice.call(arguments,2);if(this.$el.empty(),n=t&&t.length,this._isEmpty=!n,!n&&this.templates.empty)this.$el.html(function(){var t=[].slice.call(arguments,0);return t=[{query:e,isEmpty:!0}].concat(t),c.templates.empty.apply(this,t)}.apply(this,l)).prepend(c.templates.header?h.apply(this,l):null).append(c.templates.footer?p.apply(this,l):null);else if(n)this.$el.html(function(){var e,n,l=[].slice.call(arguments,0),h=this,p=u.suggestions.replace("%PREFIX%",this.cssClasses.prefix).replace("%SUGGESTIONS%",this.cssClasses.suggestions);return e=a.element(p).css(this.css.suggestions),n=o.map(t,f),e.append.apply(e,n),e;function f(e){var t,n=u.suggestion.replace("%PREFIX%",h.cssClasses.prefix).replace("%SUGGESTION%",h.cssClasses.suggestion);return(t=a.element(n).attr({role:"option",id:["option",Math.floor(1e8*Math.random())].join("-")}).append(c.templates.suggestion.apply(this,[e].concat(l)))).data(i,c.name),t.data(r,c.displayFn(e)||void 0),t.data(s,JSON.stringify(e)),t.children().each(function(){a.element(this).css(h.css.suggestionChild)}),t}}.apply(this,l)).prepend(c.templates.header?h.apply(this,l):null).append(c.templates.footer?p.apply(this,l):null);else if(t&&!Array.isArray(t))throw new TypeError("suggestions must be an array");this.$menu&&this.$menu.addClass(this.cssClasses.prefix+(n?"with":"without")+"-"+this.name).removeClass(this.cssClasses.prefix+(n?"without":"with")+"-"+this.name),this.trigger("rendered",e)}function h(){var t=[].slice.call(arguments,0);return t=[{query:e,isEmpty:!n}].concat(t),c.templates.header.apply(this,t)}function p(){var t=[].slice.call(arguments,0);return t=[{query:e,isEmpty:!n}].concat(t),c.templates.footer.apply(this,t)}},getRoot:function(){return this.$el},update:function(e){function t(t){if(!this.canceled&&e===this.query){var n=[].slice.call(arguments,1);this.cacheSuggestions(e,t,n),this._render.apply(this,[e,t].concat(n))}}if(this.query=e,this.canceled=!1,this.shouldFetchFromCache(e))t.apply(this,[this.cachedSuggestions].concat(this.cachedRenderExtraArgs));else{var n=this,i=function(){n.canceled||n.source(e,t.bind(n))};if(this.debounce){clearTimeout(this.debounceTimeout),this.debounceTimeout=setTimeout(function(){n.debounceTimeout=null,i()},this.debounce)}else i()}},cacheSuggestions:function(e,t,n){this.cachedQuery=e,this.cachedSuggestions=t,this.cachedRenderExtraArgs=n},shouldFetchFromCache:function(e){return this.cache&&this.cachedQuery===e&&this.cachedSuggestions&&this.cachedSuggestions.length},clearCachedSuggestions:function(){delete this.cachedQuery,delete this.cachedSuggestions,delete this.cachedRenderExtraArgs},cancel:function(){this.canceled=!0},clear:function(){this.$el&&(this.cancel(),this.$el.empty(),this.trigger("rendered",""))},isEmpty:function(){return this._isEmpty},destroy:function(){this.clearCachedSuggestions(),this.$el=null}}),e.exports=h},9549(e,t){!function(e){function t(e,t,n){var i;return t&&"object"==typeof t&&(void 0!==t[e]?i=t[e]:n&&t.get&&"function"==typeof t.get&&(i=t.get(e))),i}e.Template=function(e,t,n,i){e=e||{},this.r=e.code||this.r,this.c=n,this.options=i||{},this.text=t||"",this.partials=e.partials||{},this.subs=e.subs||{},this.buf=""},e.Template.prototype={r:function(e,t,n){return""},v:function(e){return e=u(e),a.test(e)?e.replace(n,"&").replace(i,"<").replace(r,">").replace(s,"'").replace(o,"""):e},t:u,render:function(e,t,n){return this.ri([e],t||{},n)},ri:function(e,t,n){return this.r(e,t,n)},ep:function(e,t){var n=this.partials[e],i=t[n.name];if(n.instance&&n.base==i)return n.instance;if("string"==typeof i){if(!this.c)throw new Error("No compiler available.");i=this.c.compile(i,this.options)}if(!i)return null;if(this.partials[e].base=i,n.subs){for(key in t.stackText||(t.stackText={}),n.subs)t.stackText[key]||(t.stackText[key]=void 0!==this.activeSub&&t.stackText[this.activeSub]?t.stackText[this.activeSub]:this.text);i=function(e,t,n,i,r,s){function o(){}function a(){}var u;o.prototype=e,a.prototype=e.subs;var c=new o;for(u in c.subs=new a,c.subsText={},c.buf="",i=i||{},c.stackSubs=i,c.subsText=s,t)i[u]||(i[u]=t[u]);for(u in i)c.subs[u]=i[u];for(u in r=r||{},c.stackPartials=r,n)r[u]||(r[u]=n[u]);for(u in r)c.partials[u]=r[u];return c}(i,n.subs,n.partials,this.stackSubs,this.stackPartials,t.stackText)}return this.partials[e].instance=i,i},rp:function(e,t,n,i){var r=this.ep(e,n);return r?r.ri(t,n,i):""},rs:function(e,t,n){var i=e[e.length-1];if(c(i))for(var r=0;r=0;u--)if(void 0!==(s=t(e,n[u],a))){o=!0;break}return o?(r||"function"!=typeof s||(s=this.mv(s,n,i)),s):!r&&""},ls:function(e,t,n,i,r){var s=this.options.delimiters;return this.options.delimiters=r,this.b(this.ct(u(e.call(t,i)),t,n)),this.options.delimiters=s,!1},ct:function(e,t,n){if(this.options.disableLambda)throw new Error("Lambda features disabled.");return this.c.compile(e,this.options).render(t,n)},b:function(e){this.buf+=e},fl:function(){var e=this.buf;return this.buf="",e},ms:function(e,t,n,i,r,s,o){var a,u=t[t.length-1],c=e.call(u);return"function"==typeof c?!!i||(a=this.activeSub&&this.subsText&&this.subsText[this.activeSub]?this.subsText[this.activeSub]:this.text,this.ls(c,u,n,a.substring(r,s),o)):c},mv:function(e,t,n){var i=t[t.length-1],r=e.call(i);return"function"==typeof r?this.ct(u(r.call(i)),i,n):r},sub:function(e,t,n,i){var r=this.subs[e];r&&(this.activeSub=e,r(t,n,this,i),this.activeSub=!1)}};var n=/&/g,i=//g,s=/\'/g,o=/\"/g,a=/[&<>\"\']/;function u(e){return String(null==e?"":e)}var c=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)}}(t)}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4250.a201ce1c.js.LICENSE.txt b/docs/assets/js/4250.a201ce1c.js.LICENSE.txt
deleted file mode 100644
index 1cf473c23ce..00000000000
--- a/docs/assets/js/4250.a201ce1c.js.LICENSE.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*!
- * lunr.Builder
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.Index
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.Pipeline
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.Set
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.TokenSet
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.Vector
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.stemmer
- * Copyright (C) 2020 Oliver Nightingale
- * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
- */
-
-/*!
- * lunr.stopWordFilter
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.tokenizer
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.trimmer
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/*!
- * lunr.utils
- * Copyright (C) 2020 Oliver Nightingale
- */
-
-/**
- * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9
- * Copyright (C) 2020 Oliver Nightingale
- * @license MIT
- */
diff --git a/docs/assets/js/4291.9c7472fc.js b/docs/assets/js/4291.9c7472fc.js
deleted file mode 100644
index a4dd750bc95..00000000000
--- a/docs/assets/js/4291.9c7472fc.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4291,6672],{4291(e,t,r){r.r(t),r.d(t,{getDatabase:()=>wa,hasIndexedDB:()=>ba});var a=r(7329);function n(e,t){var r=e.get(t);if(void 0===r)throw new Error("missing value from map "+t);return r}function i(e,t,r,a){var n=e.get(t);return void 0===n?(n=r(),e.set(t,n)):a&&a(n),n}function s(e){return Object.assign({},e)}function o(e,t=!1){if(!e)return e;if(!t&&Array.isArray(e))return e.sort((e,t)=>"string"==typeof e&&"string"==typeof t?e.localeCompare(t):"object"==typeof e?1:-1).map(e=>o(e,t));if("object"==typeof e&&!Array.isArray(e)){var r={};return Object.keys(e).sort((e,t)=>e.localeCompare(t)).forEach(a=>{r[a]=o(e[a],t)}),r}return e}var c=function e(t){if(!t)return t;if(null===t||"object"!=typeof t)return t;if(Array.isArray(t)){for(var r=new Array(t.length),a=r.length;a--;)r[a]=e(t[a]);return r}var n={};for(var i in t)n[i]=e(t[i]);return n};function u(e,t,r){return Object.defineProperty(e,t,{get:function(){return r}}),r}var h=e=>{var t=typeof e;return null!==e&&("object"===t||"function"===t)},l=new Set(["__proto__","prototype","constructor"]),d=new Set("0123456789");function m(e){var t=[],r="",a="start",n=!1;for(var i of e)switch(i){case"\\":if("index"===a)throw new Error("Invalid character in an index");if("indexEnd"===a)throw new Error("Invalid character after an index");n&&(r+=i),a="property",n=!n;break;case".":if("index"===a)throw new Error("Invalid character in an index");if("indexEnd"===a){a="property";break}if(n){n=!1,r+=i;break}if(l.has(r))return[];t.push(r),r="",a="property";break;case"[":if("index"===a)throw new Error("Invalid character in an index");if("indexEnd"===a){a="index";break}if(n){n=!1,r+=i;break}if("property"===a){if(l.has(r))return[];t.push(r),r=""}a="index";break;case"]":if("index"===a){t.push(Number.parseInt(r,10)),r="",a="indexEnd";break}if("indexEnd"===a)throw new Error("Invalid character after an index");default:if("index"===a&&!d.has(i))throw new Error("Invalid character in an index");if("indexEnd"===a)throw new Error("Invalid character after an index");"start"===a&&(a="property"),n&&(n=!1,r+="\\"),r+=i}switch(n&&(r+="\\"),a){case"property":if(l.has(r))return[];t.push(r);break;case"index":throw new Error("Index was not closed");case"start":t.push("")}return t}function f(e,t){if("number"!=typeof t&&Array.isArray(e)){var r=Number.parseInt(t,10);return Number.isInteger(r)&&e[r]===e[t]}return!1}function p(e,t){if(f(e,t))throw new Error("Cannot use string index")}function v(e,t,r){if(Array.isArray(t)&&(t=t.join(".")),!t.includes(".")&&!t.includes("["))return e[t];if(!h(e)||"string"!=typeof t)return void 0===r?e:r;var a=m(t);if(0===a.length)return r;for(var n=0;n!1,deepFreezeWhenDevMode:e=>e,tunnelErrorMessage:e=>"\n RxDB Error-Code: "+e+".\n Hint: Error messages are not included in RxDB core to reduce build size.\n To show the full error messages and to ensure that you do not make any mistakes when using RxDB,\n use the dev-mode plugin when you are in development mode: https://rxdb.info/dev-mode.html?console=error\n "};function _(e,t,r){return"\n"+e+"\n"+function(e){var t="";return 0===Object.keys(e).length?t:(t+="-".repeat(20)+"\n",t+="Parameters:\n",t+=Object.keys(e).map(t=>{var r="[object Object]";try{r="errors"===t?e[t].map(e=>JSON.stringify(e,Object.getOwnPropertyNames(e))):JSON.stringify(e[t],function(e,t){return void 0===t?null:t},2)}catch(a){}return t+": "+r}).join("\n"),t+="\n")}(r)}var k=function(e){function t(t,r,a={}){var n,i=_(r,0,a);return(n=e.call(this,i)||this).code=t,n.message=i,n.url=E(t),n.parameters=a,n.rxdb=!0,n}return(0,w.A)(t,e),t.prototype.toString=function(){return this.message},(0,b.A)(t,[{key:"name",get:function(){return"RxError ("+this.code+")"}},{key:"typeError",get:function(){return!1}}])}((0,D.A)(Error)),I=function(e){function t(t,r,a={}){var n,i=_(r,0,a);return(n=e.call(this,i)||this).code=t,n.message=i,n.url=E(t),n.parameters=a,n.rxdb=!0,n}return(0,w.A)(t,e),t.prototype.toString=function(){return this.message},(0,b.A)(t,[{key:"name",get:function(){return"RxTypeError ("+this.code+")"}},{key:"typeError",get:function(){return!0}}])}((0,D.A)(TypeError));function E(e){return"https://rxdb.info/errors.html?console=errors#"+e}function C(e){return"\nFind out more about this error here: "+E(e)+" \n"}function R(e,t){return new k(e,x.tunnelErrorMessage(e)+C(e),t)}function O(e,t){return new I(e,x.tunnelErrorMessage(e)+C(e),t)}function S(e){return!(!e||409!==e.status)&&e}var j={409:"document write conflict",422:"schema validation error",510:"attachment data missing"};function P(e){return R("COL20",{name:j[e.status],document:e.documentId,writeError:e})}var $=/\./g,N=r(8342);function B(e,t){var r=t;return r="properties."+(r=r.replace($,".properties.")),v(e,r=(0,N.L$)(r))}function M(e){return"string"==typeof e?e:e.key}function A(e,t){if("string"==typeof e.primaryKey)return t[e.primaryKey];var r=e.primaryKey;return r.fields.map(e=>{var r=v(t,e);if(void 0===r)throw R("DOC18",{args:{field:e,documentData:t}});return r}).join(r.separator)}function q(e){var t=M((e=s(e)).primaryKey);e.properties=s(e.properties),e.additionalProperties=!1,Object.prototype.hasOwnProperty.call(e,"keyCompression")||(e.keyCompression=!1),e.indexes=e.indexes?e.indexes.slice(0):[],e.required=e.required?e.required.slice(0):[],e.encrypted=e.encrypted?e.encrypted.slice(0):[],e.properties._rev={type:"string",minLength:1},e.properties._attachments={type:"object"},e.properties._deleted={type:"boolean"},e.properties._meta=T,e.required=e.required?e.required.slice(0):[],e.required.push("_deleted"),e.required.push("_rev"),e.required.push("_meta"),e.required.push("_attachments"),e.required.push(t),e.required=e.required.filter(e=>!e.includes(".")).filter((e,t,r)=>r.indexOf(e)===t),e.version=e.version||0;var r=e.indexes.map(e=>{var r=(0,g.k_)(e)?e.slice(0):[e];return r.includes(t)||r.push(t),"_deleted"!==r[0]&&r.unshift("_deleted"),r});0===r.length&&r.push(function(e){return["_deleted",e]}(t)),r.push(["_meta.lwt",t]),e.internalIndexes&&e.internalIndexes.map(e=>{r.push(e)});var a=new Set;return r.filter(e=>{var t=e.join(",");return!a.has(t)&&(a.add(t),!0)}),e.indexes=r,e}var T={type:"object",properties:{lwt:{type:"number",minimum:1,maximum:1e15,multipleOf:.01}},additionalProperties:!0,required:["lwt"]};var L="docs",Q="changes",W="attachments",F="dexie",H=new Map,K=new Map;var z="__";function Z(e){var t=e.split(".");if(t.length>1)return t.map(e=>Z(e)).join(".");if(e.startsWith("|")){var r=e.substring(1);return z+r}return e}function U(e){var t=e.split(".");return t.length>1?t.map(e=>U(e)).join("."):e.startsWith(z)?"|"+e.substring(2):e}function V(e,t){if(!t)return t;var r=s(t);return r=G(r),e.forEach(e=>{var a=v(t,e)?"1":"0",n=Z(e);y(r,n,a)}),r}function J(e,t){return t?(t=X(t=s(t)),e.forEach(e=>{var r=v(t,e);y(t,e,"1"===r)}),t):t}function G(e){if(!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e)return e;if(Array.isArray(e))return e.map(e=>G(e));if("object"==typeof e){var t={};return Object.entries(e).forEach(([e,r])=>{"object"==typeof r&&(r=G(r)),t[Z(e)]=r}),t}}function X(e){if(!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e)return e;if(Array.isArray(e))return e.map(e=>X(e));if("object"==typeof e){var t={};return Object.entries(e).forEach(([r,a])=>{("object"==typeof a||Array.isArray(e))&&(a=X(a)),t[U(r)]=a}),t}}function Y(e){var t=[],r=M(e.primaryKey);t.push([r]),t.push(["_deleted",r]),e.indexes&&e.indexes.forEach(e=>{var r=(0,g.$r)(e);t.push(r)}),t.push(["_meta.lwt",r]),t.push(["_meta.lwt"]);var a=(t=t.map(e=>e.map(e=>Z(e)))).map(e=>1===e.length?e[0]:"["+e.join("+")+"]");return(a=a.filter((e,t,r)=>r.indexOf(e)===t)).join(", ")}async function ee(e,t){var r=await e;return(await r.dexieTable.bulkGet(t)).map(e=>J(r.booleanIndexes,e))}function te(e,t){return e+"||"+t}function re(e){var t=new Set,r=[];return e.indexes?(e.indexes.forEach(a=>{(0,g.$r)(a).forEach(a=>{t.has(a)||(t.add(a),"boolean"===B(e,a).type&&r.push(a))})}),r.push("_deleted"),(0,g.jw)(r)):r}var ae=r(9092),ne=0;function ie(){var e=Date.now();(e+=.01)<=ne&&(e=ne+.01);var t=parseFloat(e.toFixed(2));return ne=t,t}var se,oe={};var ce=async function(e){var t=(new TextEncoder).encode(e),r=await function(){if(se)return se;if("undefined"==typeof crypto||void 0===crypto.subtle||"function"!=typeof crypto.subtle.digest)throw R("UT8",{args:{typeof_crypto:typeof crypto,typeof_crypto_subtle:typeof crypto?.subtle,typeof_crypto_subtle_digest:typeof crypto?.subtle?.digest}});return se=crypto.subtle.digest.bind(crypto.subtle)}()("SHA-256",t);return Array.prototype.map.call(new Uint8Array(r),e=>("00"+e.toString(16)).slice(-2)).join("")};var ue=r(4134),he=ue.Dr,le=!1;async function de(){return le?he:(le=!0,he=(async()=>!(!oe.premium||"string"!=typeof oe.premium||"6da4936d1425ff3a5c44c02342c6daf791d266be3ae8479b8ec59e261df41b93"!==await ce(oe.premium)))())}var me=r(3337),fe=String.fromCharCode(65535),pe=Number.MIN_SAFE_INTEGER;function ve(e,t){var r=t.selector,a=e.indexes?e.indexes.slice(0):[];t.index&&(a=[t.index]);var n=!!t.sort.find(e=>"desc"===Object.values(e)[0]),i=new Set;Object.keys(r).forEach(t=>{var a=B(e,t);a&&"boolean"===a.type&&Object.prototype.hasOwnProperty.call(r[t],"$eq")&&i.add(t)});var s,o=t.sort.map(e=>Object.keys(e)[0]).filter(e=>!i.has(e)).join(","),c=-1;if(a.forEach(e=>{var a=!0,u=!0,h=e.map(e=>{var t=r[e],n=t?Object.keys(t):[],i={};t&&n.length?n.forEach(e=>{if(ye.has(e)){var r=function(e,t){switch(e){case"$eq":return{startKey:t,endKey:t,inclusiveEnd:!0,inclusiveStart:!0};case"$lte":return{endKey:t,inclusiveEnd:!0};case"$gte":return{startKey:t,inclusiveStart:!0};case"$lt":return{endKey:t,inclusiveEnd:!1};case"$gt":return{startKey:t,inclusiveStart:!1};default:throw new Error("SNH")}}(e,t[e]);i=Object.assign(i,r)}}):i={startKey:u?pe:fe,endKey:a?fe:pe,inclusiveStart:!0,inclusiveEnd:!0};return void 0===i.startKey&&(i.startKey=pe),void 0===i.endKey&&(i.endKey=fe),void 0===i.inclusiveStart&&(i.inclusiveStart=!0),void 0===i.inclusiveEnd&&(i.inclusiveEnd=!0),u&&!i.inclusiveStart&&(u=!1),a&&!i.inclusiveEnd&&(a=!1),i}),l=h.map(e=>e.startKey),d=h.map(e=>e.endKey),m={index:e,startKeys:l,endKeys:d,inclusiveEnd:a,inclusiveStart:u,sortSatisfiedByIndex:!n&&o===e.filter(e=>!i.has(e)).join(","),selectorSatisfiedByIndex:we(e,t.selector,l,d)},f=function(e,t,r){var a=0,n=e=>{e>0&&(a+=e)},i=10,s=(0,g.Sd)(r.startKeys,e=>e!==pe&&e!==fe);n(s*i);var o=(0,g.Sd)(r.startKeys,e=>e!==fe&&e!==pe);n(o*i);var c=(0,g.Sd)(r.startKeys,(e,t)=>e===r.endKeys[t]);n(c*i*1.5);var u=r.sortSatisfiedByIndex?5:0;return n(u),a}(0,0,m);(f>=c||t.index)&&(c=f,s=m)}),!s)throw R("SNH",{query:t});return s}var ye=new Set(["$eq","$gt","$gte","$lt","$lte"]),ge=new Set(["$eq","$gt","$gte"]),be=new Set(["$eq","$lt","$lte"]);function we(e,t,r,a){var n=Object.entries(t).find(([t,r])=>!e.includes(t)||Object.entries(r).find(([e,t])=>!ye.has(e)));if(n)return!1;if(t.$and||t.$or)return!1;var i=[],s=new Set;for(var[o,c]of Object.entries(t)){if(!e.includes(o))return!1;var u=Object.keys(c).filter(e=>ge.has(e));if(u.length>1)return!1;var h=u[0];if(h&&s.add(o),"$eq"!==h){if(i.length>0)return!1;i.push(h)}}var l=[],d=new Set;for(var[m,f]of Object.entries(t)){if(!e.includes(m))return!1;var p=Object.keys(f).filter(e=>be.has(e));if(p.length>1)return!1;var v=p[0];if(v&&d.add(m),"$eq"!==v){if(l.length>0)return!1;l.push(v)}}var y=0;for(var g of e){for(var b of[s,d]){if(!b.has(g)&&b.size>0)return!1;b.delete(g)}if(r[y]!==a[y]&&s.size>0&&d.size>0)return!1;y++}return!0}var De,xe=r(2235),_e=r(4953),ke=r(1621),Ie=r(4192),Ee=r(695),Ce=r(2830),Re=r(2080),Oe=r(175),Se=r(8259),je=r(5912),Pe=r(6729),$e=r(6529),Ne=r(2226),Be=r(3697),Me=r(558),Ae=r(2583),qe=r(9283),Te=r(5629),Le=r(4612),Qe=r(5805),We=r(3587),Fe=r(1401),He=r(7531),Ke=!1;function ze(e){return Ke||(De=_e.ob.init({pipeline:{$sort:Ie.x,$project:Ee.C},query:{$elemMatch:Ce.J,$eq:Re.X,$nor:Oe.q,$exists:Se.P,$regex:je.W,$and:Pe.a,$gt:$e.M,$gte:Ne.f,$in:Be.o,$lt:Me.N,$lte:Ae.Q,$ne:qe.C,$nin:Te.G,$mod:Le.P,$not:Qe.E,$or:We.s,$size:Fe.I,$type:He.T}}),Ke=!0),new ke.X(e,{context:De})}function Ze(e,t){var r=M(e.primaryKey);t=s(t);var a=c(t);if("number"!=typeof a.skip&&(a.skip=0),a.selector?(a.selector=a.selector,Object.entries(a.selector).forEach(([e,t])=>{"object"==typeof t&&null!==t||(a.selector[e]={$eq:t})})):a.selector={},a.index){var n=(0,g.$r)(a.index);n.includes(r)||n.push(r),a.index=n}if(a.sort)a.sort.find(e=>{return t=e,Object.keys(t)[0]===r;var t})||(a.sort=a.sort.slice(0),a.sort.push({[r]:"asc"}));else if(a.index)a.sort=a.index.map(e=>({[e]:"asc"}));else{if(e.indexes){var i=new Set;Object.entries(a.selector).forEach(([e,t])=>{("object"!=typeof t||null===t||!!Object.keys(t).find(e=>ye.has(e)))&&i.add(e)});var o,u=-1;e.indexes.forEach(e=>{var t=(0,g.k_)(e)?e:[e],r=t.findIndex(e=>!i.has(e));r>0&&r>u&&(u=r,o=t)}),o&&(a.sort=o.map(e=>({[e]:"asc"})))}if(!a.sort)if(e.indexes&&e.indexes.length>0){var h=e.indexes[0],l=(0,g.k_)(h)?h:[h];a.sort=l.map(e=>({[e]:"asc"}))}else a.sort=[{[r]:"asc"}]}return a}function Ue(e,t){if(!t.sort)throw R("SNH",{query:t});var r=[];t.sort.forEach(e=>{var t,a,n,i=Object.keys(e)[0],s=Object.values(e)[0];r.push({key:i,direction:s,getValueFn:(t=i,a=t.split("."),n=a.length,1===n?e=>e[t]:e=>{for(var t=e,r=0;r{for(var a=0;ar.test(e)}async function Je(e,t){var r=await e.exec();return r?Array.isArray(r)?Promise.all(r.map(e=>t(e))):r instanceof Map?Promise.all([...r.values()].map(e=>t(e))):await t(r):null}function Ge(e,t){if(!t.sort)throw R("SNH",{query:t});return{query:t,queryPlan:ve(e,t)}}function Xe(e){return e===pe?-1/0:e}function Ye(e,t,r){return e.includes(t)?r===fe||!0===r?"1":"0":r}function et(e,t,r){if(!r){if("undefined"==typeof window)throw new Error("IDBKeyRange missing");r=window.IDBKeyRange}var a=t.startKeys.map((r,a)=>{var n=t.index[a];return Ye(e,n,r)}).map(Xe),n=t.endKeys.map((r,a)=>{var n=t.index[a];return Ye(e,n,r)}).map(Xe);return r.bound(a,n,!t.inclusiveStart,!t.inclusiveEnd)}async function tt(e,t){var r=await e.internals,a=t.query,n=a.skip?a.skip:0,i=n+(a.limit?a.limit:1/0),s=t.queryPlan,o=!1;s.selectorSatisfiedByIndex||(o=Ve(e.schema,t.query));var c=et(r.booleanIndexes,s,r.dexieDb._options.IDBKeyRange),u=s.index,h=[];if(await r.dexieDb.transaction("r",r.dexieTable,async e=>{var t,a=e.idbtrans.objectStore(L);t="["+u.map(e=>Z(e)).join("+")+"]";var n=a.index(t).openCursor(c);await new Promise(e=>{n.onsuccess=function(t){var a=t.target.result;if(a){var n=J(r.booleanIndexes,a.value);o&&!o(n)||h.push(n),s.sortSatisfiedByIndex&&h.length===i?e():a.continue()}else e()}})}),!s.sortSatisfiedByIndex){var l=Ue(e.schema,t.query);h=h.sort(l)}return{documents:h=h.slice(n,i)}}function rt(e){for(var t="",r=0;r0&&nt[e].forEach(e=>e(t))}async function st(e,t){for(var r of nt[e])await r(t)}async function ot(e,t){var r=(await e.findDocumentsById([t],!1))[0];return r||void 0}async function ct(e,t,r){var a=await e.bulkWrite([t],r);if(a.error.length>0)throw a.error[0];return vt(M(e.schema.primaryKey),[t],a)[0]}function ut(e,t,r,a){if(a)throw 409===a.status?R("CONFLICT",{collection:e.name,id:t,writeError:a,data:r}):422===a.status?R("VD2",{collection:e.name,id:t,writeError:a,data:r}):a}function ht(e){return{previous:e.previous,document:lt(e.document)}}function lt(e){if(!e._attachments||0===Object.keys(e._attachments).length)return e;var t=s(e);return t._attachments={},Object.entries(e._attachments).forEach(([e,r])=>{var a,n,i;t._attachments[e]=(i=(a=r).data)?{length:(n=i,atob(n).length),digest:a.digest,type:a.type}:a}),t}function dt(e){return Object.assign({},e,{_meta:s(e._meta)})}function mt(e,t,r){x.deepFreezeWhenDevMode(r);var a=M(t.schema.primaryKey),n={originalStorageInstance:t,schema:t.schema,internals:t.internals,collectionName:t.collectionName,databaseName:t.databaseName,options:t.options,async bulkWrite(r,n){for(var i=e.token,s=new Array(r.length),o=ie(),c=0;ct.bulkWrite(s,n)),m={error:[]};ft.set(m,s);var f=0===d.error.length?[]:d.error.filter(e=>!(409!==e.status||e.writeRow.previous||e.writeRow.document._deleted||!(0,me.ZN)(e.documentInDb)._deleted)||(m.error.push(e),!1));if(f.length>0){var p=new Set,v=f.map(t=>(p.add(t.documentId),{previous:t.documentInDb,document:Object.assign({},t.writeRow.document,{_rev:at(e.token,t.documentInDb)})})),y=await e.lockedRun(()=>t.bulkWrite(v,n));m.error=m.error.concat(y.error);var g=vt(a,s,m,p),b=vt(a,v,y);return g.push(...b),m}return m},query:r=>e.lockedRun(()=>t.query(r)),count:r=>e.lockedRun(()=>t.count(r)),findDocumentsById:(r,a)=>e.lockedRun(()=>t.findDocumentsById(r,a)),getAttachmentData:(r,a,n)=>e.lockedRun(()=>t.getAttachmentData(r,a,n)),getChangedDocumentsSince:t.getChangedDocumentsSince?(r,a)=>e.lockedRun(()=>t.getChangedDocumentsSince((0,me.ZN)(r),a)):void 0,cleanup:r=>e.lockedRun(()=>t.cleanup(r)),remove:()=>(e.storageInstances.delete(n),e.lockedRun(()=>t.remove())),close:()=>(e.storageInstances.delete(n),e.lockedRun(()=>t.close())),changeStream:()=>t.changeStream()};return e.storageInstances.add(n),n}var ft=new WeakMap,pt=new WeakMap;function vt(e,t,r,a){return i(pt,r,()=>{var n=[],i=ft.get(r);if(i||(i=t),r.error.length>0||a){for(var s=a||new Set,o=0;o{r.storageName===e&&r.databaseName===t.databaseName&&r.collectionName===t.collectionName&&r.version===t.schema.version&&i.next(r.eventBulk)};n.addEventListener("message",s);var o=r.changeStream(),c=!1,u=o.subscribe(r=>{c||n.postMessage({storageName:e,databaseName:t.databaseName,collectionName:t.collectionName,version:t.schema.version,eventBulk:r})});r.changeStream=function(){return i.asObservable().pipe((0,yt.X)(o))};var h=r.close.bind(r);r.close=async function(){return c=!0,u.unsubscribe(),n.removeEventListener("message",s),a||await wt(t.databaseInstanceToken,r),h()};var l=r.remove.bind(r);r.remove=async function(){return c=!0,u.unsubscribe(),n.removeEventListener("message",s),a||await wt(t.databaseInstanceToken,r),l()}}}var xt=ie(),_t=!1,kt=function(){function e(e,t,r,a,n,i,s,o){this.changes$=new ae.B,this.instanceId=xt++,this.storage=e,this.databaseName=t,this.collectionName=r,this.schema=a,this.internals=n,this.options=i,this.settings=s,this.devMode=o,this.primaryPath=M(this.schema.primaryKey)}var t=e.prototype;return t.bulkWrite=async function(e,t){Et(this),_t||await de()||console.warn(["-------------- RxDB Open Core RxStorage -------------------------------","You are using the free Dexie.js based RxStorage implementation from RxDB https://rxdb.info/rx-storage-dexie.html?console=dexie ","While this is a great option, we want to let you know that there are faster storage solutions available in our premium plugins.","For professional users and production environments, we highly recommend considering these premium options to enhance performance and reliability."," https://rxdb.info/premium/?console=dexie ","If you already purchased premium access you can disable this log by calling the setPremiumFlag() function from rxdb-premium/plugins/shared.","---------------------------------------------------------------------"].join("\n")),_t=!0,e.forEach(e=>{if(!e.document._rev||e.previous&&!e.previous._rev)throw R("SNH",{args:{row:e}})});var r=await this.internals,a={error:[]};this.devMode&&(e=e.map(e=>{var t=dt(e.document);return{previous:e.previous,document:t}}));var n,i=e.map(e=>e.document[this.primaryPath]);if(await r.dexieDb.transaction("rw",r.dexieTable,r.dexieAttachmentsTable,async()=>{var s=new Map;(await ee(this.internals,i)).forEach(e=>{var t=e;return t&&s.set(t[this.primaryPath],t),t}),n=function(e,t,r,a,n,i,s){for(var o,c=!!e.schema.attachments,u=[],h=[],l=[],d={id:(0,N.zs)(10),events:[],checkpoint:null,context:n},m=d.events,f=[],p=[],v=[],y=r.size>0,g=a.length,b=function(){var e,d=a[w],g=d.document,b=d.previous,D=g[t],x=g._deleted,_=b&&b._deleted,k=void 0;if(y&&(k=r.get(D)),k){var I=k._rev;if(!b||b&&I!==b._rev){var E={isError:!0,status:409,documentId:D,writeRow:d,documentInDb:k,context:n};return l.push(E),1}var C=c?ht(d):d;c&&(x?b&&Object.keys(b._attachments).forEach(e=>{p.push({documentId:D,attachmentId:e,digest:(0,me.ZN)(b)._attachments[e].digest})}):(Object.entries(g._attachments).find(([t,r])=>((b?b._attachments[t]:void 0)||r.data||(e={documentId:D,documentInDb:k,isError:!0,status:510,writeRow:d,attachmentId:t,context:n}),!0)),e||Object.entries(g._attachments).forEach(([e,t])=>{var r=b?b._attachments[e]:void 0;if(r){var a=C.document._attachments[e].digest;t.data&&r.digest!==a&&v.push({documentId:D,attachmentId:e,attachmentData:t,digest:t.digest})}else f.push({documentId:D,attachmentId:e,attachmentData:t,digest:t.digest})}))),e?l.push(e):(c?(h.push(ht(C)),s&&s(g)):(h.push(C),s&&s(g)),o=C);var O=null,S=null,j=null;if(_&&!x)j="INSERT",O=c?lt(g):g;else if(!b||_||x){if(!x)throw R("SNH",{args:{writeRow:d}});j="DELETE",O=(0,me.ZN)(g),S=b}else j="UPDATE",O=c?lt(g):g,S=b;var P={documentId:D,documentData:O,previousDocumentData:S,operation:j};m.push(P)}else{var $=!!x;if(c&&Object.entries(g._attachments).forEach(([t,r])=>{r.data?f.push({documentId:D,attachmentId:t,attachmentData:r,digest:r.digest}):(e={documentId:D,isError:!0,status:510,writeRow:d,attachmentId:t,context:n},l.push(e))}),e||(c?(u.push(ht(d)),i&&i(g)):(u.push(d),i&&i(g)),o=d),!$){var N={documentId:D,operation:"INSERT",documentData:c?lt(g):g,previousDocumentData:c&&b?lt(b):b};m.push(N)}}},w=0;w{o.push(e.document)}),n.bulkUpdateDocs.forEach(e=>{o.push(e.document)}),(o=o.map(e=>V(r.booleanIndexes,e))).length>0&&await r.dexieTable.bulkPut(o);var c=[];n.attachmentsAdd.forEach(e=>{c.push({id:te(e.documentId,e.attachmentId),data:e.attachmentData.data})}),n.attachmentsUpdate.forEach(e=>{c.push({id:te(e.documentId,e.attachmentId),data:e.attachmentData.data})}),await r.dexieAttachmentsTable.bulkPut(c),await r.dexieAttachmentsTable.bulkDelete(n.attachmentsRemove.map(e=>te(e.documentId,e.attachmentId)))}),(n=(0,me.ZN)(n)).eventBulk.events.length>0){var s=(0,me.ZN)(n.newestRow).document;n.eventBulk.checkpoint={id:s[this.primaryPath],lwt:s._meta.lwt},this.changes$.next(n.eventBulk)}return a},t.findDocumentsById=async function(e,t){Et(this);var r=await this.internals,a=[];return await r.dexieDb.transaction("r",r.dexieTable,async()=>{(await ee(this.internals,e)).forEach(e=>{!e||e._deleted&&!t||a.push(e)})}),a},t.query=function(e){return Et(this),tt(this,e)},t.count=async function(e){if(e.queryPlan.selectorSatisfiedByIndex){var t=await async function(e,t){var r=await e.internals,a=t.queryPlan,n=a.index,i=et(r.booleanIndexes,a,r.dexieDb._options.IDBKeyRange),s=-1;return await r.dexieDb.transaction("r",r.dexieTable,async e=>{var t,r=e.idbtrans.objectStore(L);t="["+n.map(e=>Z(e)).join("+")+"]";var a=r.index(t).count(i);s=await new Promise((e,t)=>{a.onsuccess=function(){e(a.result)},a.onerror=e=>t(e)})}),s}(this,e);return{count:t,mode:"fast"}}return{count:(await tt(this,e)).documents.length,mode:"slow"}},t.changeStream=function(){return Et(this),this.changes$.asObservable()},t.cleanup=async function(e){Et(this);var t=await this.internals;return await t.dexieDb.transaction("rw",t.dexieTable,async()=>{var r=ie()-e,a=await t.dexieTable.where("_meta.lwt").below(r).toArray(),n=[];a.forEach(e=>{"1"===e._deleted&&n.push(e[this.primaryPath])}),await t.dexieTable.bulkDelete(n)}),!0},t.getAttachmentData=async function(e,t,r){Et(this);var a=await this.internals,n=te(e,t);return await a.dexieDb.transaction("r",a.dexieAttachmentsTable,async()=>{var r=await a.dexieAttachmentsTable.get(n);if(r)return r.data;throw new Error("attachment missing documentId: "+e+" attachmentId: "+t)})},t.remove=async function(){Et(this);var e=await this.internals;return await e.dexieTable.clear(),this.close()},t.close=function(){return this.closed||(this.closed=(async()=>{this.changes$.complete(),await async function(e){var t=await e,r=K.get(e)-1;0===r?(t.dexieDb.close(),K.delete(e)):K.set(e,r)}(this.internals)})()),this.closed},e}();async function It(e,t,r){var n=function(e,t,r,n){var o="rxdb-dexie-"+e+"--"+n.version+"--"+t,c=i(H,o,()=>{var e=(async()=>{var e=s(r);e.autoOpen=!1;var t=new a.cf(o,e);r.onCreate&&await r.onCreate(t,o);var i={[L]:Y(n),[Q]:"++sequence, id",[W]:"id"};return t.version(1).stores(i),await t.open(),{dexieDb:t,dexieTable:t[L],dexieAttachmentsTable:t[W],booleanIndexes:re(n)}})();return H.set(o,c),K.set(c,0),e});return c}(t.databaseName,t.collectionName,r,t.schema),o=new kt(e,t.databaseName,t.collectionName,t.schema,n,t.options,r,t.devMode);return await Dt(F,t,o),Promise.resolve(o)}function Et(e){if(e.closed)throw new Error("RxStorageInstanceDexie is closed "+e.databaseName+"-"+e.collectionName)}var Ct="17.0.0-beta.6",Rt=function(){function e(e){this.name=F,this.rxdbVersion=Ct,this.settings=e}return e.prototype.createStorageInstance=function(e){(function(e){if(e.schema.keyCompression)throw R("UT5",{args:{params:e}});if((t=e.schema).encrypted&&t.encrypted.length>0||t.attachments&&t.attachments.encrypted)throw R("UT6",{args:{params:e}});var t;if(e.schema.attachments&&e.schema.attachments.compression)throw R("UT7",{args:{params:e}})}(e),e.schema.indexes)&&e.schema.indexes.flat().filter(e=>!e.includes(".")).forEach(t=>{if(!e.schema.required||!e.schema.required.includes(t))throw R("DXE1",{field:t,schema:e.schema})});return It(this,e,this.settings)},e}();function Ot(e={}){return new Rt(e)}var St=r(686),jt=function(){function e(e,t){if(this.jsonSchema=e,this.hashFunction=t,this.indexes=function(e){return(e.indexes||[]).map(e=>(0,g.k_)(e)?e:[e])}(this.jsonSchema),this.primaryPath=M(this.jsonSchema.primaryKey),!e.properties[this.primaryPath].maxLength)throw R("SC39",{schema:e});this.finalFields=function(e){var t=Object.keys(e.properties).filter(t=>e.properties[t].final),r=M(e.primaryKey);return t.push(r),"string"!=typeof e.primaryKey&&e.primaryKey.fields.forEach(e=>t.push(e)),t}(this.jsonSchema)}var t=e.prototype;return t.validateChange=function(e,t){this.finalFields.forEach(r=>{if(!(0,St.b)(e[r],t[r]))throw R("DOC9",{dataBefore:e,dataAfter:t,fieldName:r,schema:this.jsonSchema})})},t.getDocumentPrototype=function(){var e={},t=B(this.jsonSchema,"");return Object.keys(t).forEach(t=>{var r=t;e.__defineGetter__(t,function(){if(this.get&&"function"==typeof this.get)return this.get(r)}),Object.defineProperty(e,t+"$",{get:function(){return this.get$(r)},enumerable:!1,configurable:!1}),Object.defineProperty(e,t+"$$",{get:function(){return this.get$$(r)},enumerable:!1,configurable:!1}),Object.defineProperty(e,t+"_",{get:function(){return this.populate(r)},enumerable:!1,configurable:!1})}),u(this,"getDocumentPrototype",()=>e),e},t.getPrimaryOfDocumentData=function(e){return A(this.jsonSchema,e)},(0,b.A)(e,[{key:"version",get:function(){return this.jsonSchema.version}},{key:"defaultValues",get:function(){var e={};return Object.entries(this.jsonSchema.properties).filter(([,e])=>Object.prototype.hasOwnProperty.call(e,"default")).forEach(([t,r])=>e[t]=r.default),u(this,"defaultValues",e)}},{key:"hash",get:function(){return u(this,"hash",this.hashFunction(JSON.stringify(this.jsonSchema)))}}])}();function Pt(e,t,r=!0){r&&it("preCreateRxSchema",e);var a=q(e);a=function(e){return o(e,!0)}(a),x.deepFreezeWhenDevMode(a);var n=new jt(a,t);return it("createRxSchema",n),n}var $t=r(7708),Nt=r(8146),Bt=r(3356),Mt=r(5520),At=r(8609);function qt(e){var t=e.split("-"),r="RxDB";return t.forEach(e=>{r+=(0,N.Z2)(e)}),r+="Plugin",new Error("You are using a function which must be overwritten by a plugin.\n You should either prevent the usage of this function or add the plugin via:\n import { "+r+" } from 'rxdb/plugins/"+e+"';\n addRxPlugin("+r+");\n ")}function Tt(e){return e.documentData?e.documentData:e.previousDocumentData}function Lt(e){switch(e.operation){case"INSERT":return{operation:e.operation,id:e.documentId,doc:e.documentData,previous:null};case"UPDATE":return{operation:e.operation,id:e.documentId,doc:x.deepFreezeWhenDevMode(e.documentData),previous:e.previousDocumentData?e.previousDocumentData:"UNKNOWN"};case"DELETE":return{operation:e.operation,id:e.documentId,doc:null,previous:e.previousDocumentData}}}var Qt=new Map;function Wt(e){return i(Qt,e,()=>{for(var t=new Array(e.events.length),r=e.events,a=e.collectionName,n=e.isLocal,i=x.deepFreezeWhenDevMode,s=0;s[]);return new Promise((r,n)=>{var i={lastKnownDocumentState:e,modifier:t,resolve:r,reject:n};(0,me.ZN)(a).push(i),this.triggerRun()})},t.triggerRun=async function(){if(!0!==this.isRunning&&0!==this.queueByDocId.size){this.isRunning=!0;var e=[],t=this.queueByDocId;this.queueByDocId=new Map,await Promise.all(Array.from(t.entries()).map(async([t,r])=>{var a,n,i,s=(a=r.map(e=>e.lastKnownDocumentState),n=a[0],i=rt(n._rev),a.forEach(e=>{var t=rt(e._rev);t>i&&(n=e,i=t)}),n),o=s;for(var u of r)try{o=await u.modifier(c(o))}catch(h){u.reject(h),u.reject=()=>{},u.resolve=()=>{}}try{await this.preWrite(o,s)}catch(h){return void r.forEach(e=>e.reject(h))}e.push({previous:s,document:o})}));var r=e.length>0?await this.storageInstance.bulkWrite(e,"incremental-write"):{error:[]};return await Promise.all(vt(this.primaryPath,e,r).map(e=>{var r=e[this.primaryPath];this.postWrite(e),n(t,r).forEach(t=>t.resolve(e))})),r.error.forEach(e=>{var r=e.documentId,a=n(t,r),s=S(e);if(s){var o=i(this.queueByDocId,r,()=>[]);a.reverse().forEach(e=>{e.lastKnownDocumentState=(0,me.ZN)(s.documentInDb),(0,me.ZN)(o).unshift(e)})}else{var c=P(e);a.forEach(e=>e.reject(c))}}),this.isRunning=!1,this.triggerRun()}},e}();function Ht(e){return async t=>{var r=function(e){return Object.assign({},e,{_meta:void 0,_deleted:void 0,_rev:void 0})}(t);r._deleted=t._deleted;var a=await e(r),n=Object.assign({},a,{_meta:t._meta,_attachments:t._attachments,_rev:t._rev,_deleted:void 0!==a._deleted?a._deleted:t._deleted});return void 0===n._deleted&&(n._deleted=!1),n}}var Kt={get primaryPath(){if(this.isInstanceOfRxDocument)return this.collection.schema.primaryPath},get primary(){var e=this;if(e.isInstanceOfRxDocument)return e._data[e.primaryPath]},get revision(){if(this.isInstanceOfRxDocument)return this._data._rev},get deleted$(){if(this.isInstanceOfRxDocument)return this.$.pipe((0,$t.T)(e=>e._data._deleted))},get deleted$$(){var e=this;return e.collection.database.getReactivityFactory().fromObservable(e.deleted$,e.getLatest().deleted,e.collection.database)},get deleted(){if(this.isInstanceOfRxDocument)return this._data._deleted},getLatest(){var e=this.collection._docCache.getLatestDocumentData(this.primary);return this.collection._docCache.getCachedRxDocument(e)},get $(){var e=this.primary;return this.collection.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal),(0,$t.T)(t=>t.events.find(t=>t.documentId===e)),(0,Nt.p)(e=>!!e),(0,$t.T)(e=>Tt((0,me.ZN)(e))),(0,Bt.Z)(this.collection._docCache.getLatestDocumentData(e)),(0,Mt.F)((e,t)=>e._rev===t._rev),(0,$t.T)(e=>this.collection._docCache.getCachedRxDocument(e)),(0,At.t)(me.bz))},get $$(){var e=this;return e.collection.database.getReactivityFactory().fromObservable(e.$,e.getLatest()._data,e.collection.database)},get$(e){if(x.isDevMode()){if(e.includes(".item."))throw R("DOC1",{path:e});if(e===this.primaryPath)throw R("DOC2");if(this.collection.schema.finalFields.includes(e))throw R("DOC3",{path:e});if(!B(this.collection.schema.jsonSchema,e))throw R("DOC4",{path:e})}return this.$.pipe((0,$t.T)(t=>v(t,e)),(0,Mt.F)())},get$$(e){var t=this.get$(e);return this.collection.database.getReactivityFactory().fromObservable(t,this.getLatest().get(e),this.collection.database)},populate(e){var t=B(this.collection.schema.jsonSchema,e),r=this.get(e);if(!r)return ue.$A;if(!t)throw R("DOC5",{path:e});if(!t.ref)throw R("DOC6",{path:e,schemaObj:t});var a=this.collection.database.collections[t.ref];if(!a)throw R("DOC7",{ref:t.ref,path:e,schemaObj:t});return"array"===t.type?a.findByIds(r).exec().then(e=>{var t=e.values();return Array.from(t)}):a.findOne(r).exec()},get(e){return Ut(this,e)},toJSON(e=!1){if(e)return x.deepFreezeWhenDevMode(this._data);var t=s(this._data);return delete t._rev,delete t._attachments,delete t._deleted,delete t._meta,x.deepFreezeWhenDevMode(t)},toMutableJSON(e=!1){return c(this.toJSON(e))},update(e){throw qt("update")},incrementalUpdate(e){throw qt("update")},updateCRDT(e){throw qt("crdt")},putAttachment(){throw qt("attachments")},putAttachmentBase64(){throw qt("attachments")},getAttachment(){throw qt("attachments")},allAttachments(){throw qt("attachments")},get allAttachments$(){throw qt("attachments")},async modify(e,t){var r=this._data,a=await Ht(e)(r);return this._saveData(a,r)},incrementalModify(e,t){return this.collection.incrementalWriteQueue.addWrite(this._data,Ht(e)).then(e=>this.collection._docCache.getCachedRxDocument(e))},patch(e){var t=this._data,r=c(t);return Object.entries(e).forEach(([e,t])=>{r[e]=t}),this._saveData(r,t)},incrementalPatch(e){return this.incrementalModify(t=>(Object.entries(e).forEach(([e,r])=>{t[e]=r}),t))},async _saveData(e,t){if(e=s(e),this._data._deleted)throw R("DOC11",{id:this.primary,document:this});await Zt(this.collection,e,t);var r=[{previous:t,document:e}],a=await this.collection.storageInstance.bulkWrite(r,"rx-document-save-data"),n=a.error[0];return ut(this.collection,this.primary,e,n),await this.collection._runHooks("post","save",e,this),this.collection._docCache.getCachedRxDocument(vt(this.collection.schema.primaryPath,r,a)[0])},async remove(){if(this.deleted)return Promise.reject(R("DOC13",{document:this,id:this.primary}));var e=await this.collection.bulkRemove([this]);if(e.error.length>0){var t=e.error[0];ut(this.collection,this.primary,this._data,t)}return e.success[0]},incrementalRemove(){return this.incrementalModify(async e=>(await this.collection._runHooks("pre","remove",e,this),e._deleted=!0,e)).then(async e=>(await this.collection._runHooks("post","remove",e._data,e),e))},close(){throw R("DOC14")}};function zt(e=Kt){var t=function(e,t){this.collection=e,this._data=t,this._propertyCache=new Map,this.isInstanceOfRxDocument=!0};return t.prototype=e,t}function Zt(e,t,r){return t._meta=Object.assign({},r._meta,t._meta),x.isDevMode()&&e.schema.validateChange(r,t),e._runHooks("pre","save",t,r)}function Ut(e,t){return i(e._propertyCache,t,()=>{var r=v(e._data,t);return"object"!=typeof r||null===r||Array.isArray(r)?x.deepFreezeWhenDevMode(r):new Proxy(s(r),{get(r,a){if("string"!=typeof a)return r[a];var n=a.charAt(a.length-1);if("$"===n){if(a.endsWith("$$")){var i=a.slice(0,-2);return e.get$$((0,N.L$)(t+"."+i))}var s=a.slice(0,-1);return e.get$((0,N.L$)(t+"."+s))}if("_"===n){var o=a.slice(0,-1);return e.populate((0,N.L$)(t+"."+o))}var c=r[a];return"number"==typeof c||"string"==typeof c||"boolean"==typeof c?c:Ut(e,(0,N.L$)(t+"."+a))}})})}var Vt=r(2198),Jt=r(4157),Gt=r(2442),Xt=r(6114);function Yt(e,t){return t.sort&&0!==t.sort.length?t.sort.map(e=>Object.keys(e)[0]):[e]}var er=new WeakMap;function tr(e,t){if(!e.collection.database.eventReduce)return{runFullQueryAgain:!0};for(var r=function(e){return i(er,e,()=>{var t=e.collection,r=Ze(t.storageInstance.schema,c(e.mangoQuery)),a=t.schema.primaryPath,n=Ue(t.schema.jsonSchema,r),i=Ve(t.schema.jsonSchema,r);return{primaryKey:e.collection.schema.primaryPath,skip:r.skip,limit:r.limit,sortFields:Yt(a,r),sortComparator:(t,r)=>{var a={docA:t,docB:r,rxQuery:e};return n(a.docA,a.docB)},queryMatcher:t=>i({doc:t,rxQuery:e}.doc)}})}(e),a=(0,me.ZN)(e._result).docsData.slice(0),n=(0,me.ZN)(e._result).docsDataMap,s=!1,o=[],u=0;u{var t={queryParams:r,changeEvent:e,previousResults:a,keyDocumentMap:n},i=(0,Xt.kC)(t);return"runFullQueryAgain"===i||("doNothing"!==i?(s=!0,(0,Xt.Cs)(i,r,e,a,n),!1):void 0)});return l?{runFullQueryAgain:!0}:{runFullQueryAgain:!1,changed:s,newResults:a}}var rr=function(){function e(){this._map=new Map}return e.prototype.getByQuery=function(e){var t=e.toString();return i(this._map,t,()=>e)},e}();function ar(e,t){t.uncached=!0;var r=t.toString();e._map.delete(r)}function nr(e){return e.refCount$.observers.length}var ir,sr,or=(ir=100,sr=3e4,(e,t)=>{if(!(t._map.size0||(0===i._lastEnsureEqual&&i._creationTimee._lastEnsureEqual-t._lastEnsureEqual).slice(0,s).forEach(e=>ar(t,e))}}),cr=new WeakSet;var ur=function(){function e(e,t,r){this.cacheItemByDocId=new Map,this.tasks=new Set,this.registry="function"==typeof FinalizationRegistry?new FinalizationRegistry(e=>{var t=e.docId,r=this.cacheItemByDocId.get(t);r&&(r[0].delete(e.revisionHeight+e.lwt+""),0===r[0].size&&this.cacheItemByDocId.delete(t))}):void 0,this.primaryPath=e,this.changes$=t,this.documentCreator=r,t.subscribe(e=>{this.tasks.add(()=>{for(var t=this.cacheItemByDocId,r=0;r{this.processTasks()})})}var t=e.prototype;return t.processTasks=function(){0!==this.tasks.size&&(Array.from(this.tasks).forEach(e=>e()),this.tasks.clear())},t.getLatestDocumentData=function(e){return this.processTasks(),n(this.cacheItemByDocId,e)[1]},t.getLatestDocumentDataIfExists=function(e){this.processTasks();var t=this.cacheItemByDocId.get(e);if(t)return t[1]},(0,b.A)(e,[{key:"getCachedRxDocuments",get:function(){return u(this,"getCachedRxDocuments",hr(this))}},{key:"getCachedRxDocument",get:function(){var e=hr(this);return u(this,"getCachedRxDocument",t=>e([t])[0])}}])}();function hr(e){var t=e.primaryPath,r=e.cacheItemByDocId,a=e.registry,n=x.deepFreezeWhenDevMode,i=e.documentCreator;return s=>{for(var o=new Array(s.length),c=[],u=0;u0&&a&&(e.tasks.add(()=>{for(var e=0;e{e.processTasks()})),o}}function lr(e,t){return(0,e.getCachedRxDocuments)(t)}var dr="function"==typeof WeakRef?function(e){return new WeakRef(e)}:function(e){return{deref:()=>e}};var mr=function(){function e(e,t,r){this.time=ie(),this.query=e,this.count=r,this.documents=lr(this.query.collection._docCache,t)}return e.prototype.getValue=function(e){var t=this.query.op;if("count"===t)return this.count;if("findOne"===t){var r=0===this.documents.length?null:this.documents[0];if(!r&&e)throw R("QU10",{collection:this.query.collection.name,query:this.query.mangoQuery,op:t});return r}return"findByIds"===t?this.docsMap:this.documents.slice(0)},(0,b.A)(e,[{key:"docsData",get:function(){return u(this,"docsData",this.documents.map(e=>e._data))}},{key:"docsDataMap",get:function(){var e=new Map;return this.documents.forEach(t=>{e.set(t.primary,t._data)}),u(this,"docsDataMap",e)}},{key:"docsMap",get:function(){for(var e=new Map,t=this.documents,r=0;r"string"!=typeof e))return r.$eq}return!1}(this.collection.schema.primaryPath,t)}var t=e.prototype;return t._setResultData=function(e){if(void 0===e)throw R("QU18",{database:this.collection.database.name,collection:this.collection.name});if("number"!=typeof e){e instanceof Map&&(e=Array.from(e.values()));var t=new mr(this,e,e.length);this._result=t}else this._result=new mr(this,[],e)},t._execOverDatabase=async function(){if(this._execOverDatabaseCount=this._execOverDatabaseCount+1,"count"===this.op){var e=this.getPreparedQuery(),t=await this.collection.storageInstance.count(e);if("slow"!==t.mode||this.collection.database.allowSlowCount)return{result:t.count,counter:this.collection._changeEventBuffer.getCounter()};throw R("QU14",{collection:this.collection,queryObj:this.mangoQuery})}if("findByIds"===this.op){var r=(0,me.ZN)(this.mangoQuery.selector)[this.collection.schema.primaryPath].$in,a=new Map,n=[];if(r.forEach(e=>{var t=this.collection._docCache.getLatestDocumentDataIfExists(e);if(t){if(!t._deleted){var r=this.collection._docCache.getCachedRxDocument(t);a.set(e,r)}}else n.push(e)}),n.length>0)(await this.collection.storageInstance.findDocumentsById(n,!1)).forEach(e=>{var t=this.collection._docCache.getCachedRxDocument(e);a.set(t.primary,t)});return{result:a,counter:this.collection._changeEventBuffer.getCounter()}}var i=await gr(this);return{result:i.docs,counter:i.counter}},t.exec=async function(e){if(e&&"findOne"!==this.op)throw R("QU9",{collection:this.collection.name,query:this.mangoQuery,op:this.op});return await yr(this),(0,me.ZN)(this._result).getValue(e)},t.toString=function(){var e=o({op:this.op,query:Ze(this.collection.schema.jsonSchema,this.mangoQuery),other:this.other},!0),t=JSON.stringify(e);return this.toString=()=>t,t},t.getPreparedQuery=function(){var e={rxQuery:this,mangoQuery:Ze(this.collection.schema.jsonSchema,this.mangoQuery)};e.mangoQuery.selector._deleted={$eq:!1},e.mangoQuery.index&&e.mangoQuery.index.unshift("_deleted"),it("prePrepareQuery",e);var t=Ge(this.collection.schema.jsonSchema,e.mangoQuery);return this.getPreparedQuery=()=>t,t},t.doesDocumentDataMatch=function(e){return!e._deleted&&this.queryMatcher(e)},t.remove=async function(){var e=await this.exec();if(Array.isArray(e)){var t=await this.collection.bulkRemove(e);if(t.error.length>0)throw P(t.error[0]);return t.success}return e.remove()},t.incrementalRemove=function(){return Je(this.asRxQuery,e=>e.incrementalRemove())},t.update=function(e){throw qt("update")},t.patch=function(e){return Je(this.asRxQuery,t=>t.patch(e))},t.incrementalPatch=function(e){return Je(this.asRxQuery,t=>t.incrementalPatch(e))},t.modify=function(e){return Je(this.asRxQuery,t=>t.modify(e))},t.incrementalModify=function(e){return Je(this.asRxQuery,t=>t.incrementalModify(e))},t.where=function(e){throw qt("query-builder")},t.sort=function(e){throw qt("query-builder")},t.skip=function(e){throw qt("query-builder")},t.limit=function(e){throw qt("query-builder")},(0,b.A)(e,[{key:"$",get:function(){if(!this._$){var e=this.collection.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal),(0,Bt.Z)(null),(0,Gt.Z)(()=>yr(this)),(0,$t.T)(()=>this._result),(0,At.t)(me.bz),(0,Mt.F)((e,t)=>!(!e||e.time!==(0,me.ZN)(t).time)),(0,Nt.p)(e=>!!e),(0,$t.T)(e=>(0,me.ZN)(e).getValue()));this._$=(0,Jt.h)(e,this.refCount$.pipe((0,Nt.p)(()=>!1)))}return this._$}},{key:"$$",get:function(){return this.collection.database.getReactivityFactory().fromObservable(this.$,void 0,this.collection.database)}},{key:"queryMatcher",get:function(){this.collection.schema.jsonSchema;return u(this,"queryMatcher",Ve(0,Ze(this.collection.schema.jsonSchema,this.mangoQuery)))}},{key:"asRxQuery",get:function(){return this}}])}();function vr(e,t,r,a){it("preCreateRxQuery",{op:e,queryObj:t,collection:r,other:a});var n,i,s=new pr(e,t,r,a);return s=(n=s).collection._queryCache.getByQuery(n),i=r,cr.has(i)||(cr.add(i),(0,ue.dY)().then(()=>(0,ue.Ve)(200)).then(()=>{i.closed||i.cacheReplacementPolicy(i,i._queryCache),cr.delete(i)})),s}async function yr(e){return e.collection.awaitBeforeReads.size>0&&await Promise.all(Array.from(e.collection.awaitBeforeReads).map(e=>e())),e._ensureEqualQueue=e._ensureEqualQueue.then(()=>function(e){if(e._lastEnsureEqual=ie(),e.collection.database.closed||function(e){var t=e.asRxQuery.collection._changeEventBuffer.getCounter();return e._latestChangeEvent>=t}(e))return ue.Dr;var t=!1,r=!1;-1===e._latestChangeEvent&&(r=!0);if(!r){var a=e.asRxQuery.collection._changeEventBuffer.getFrom(e._latestChangeEvent+1);if(null===a)r=!0;else{e._latestChangeEvent=e.asRxQuery.collection._changeEventBuffer.getCounter();var n=e.asRxQuery.collection._changeEventBuffer.reduceByLastOfDoc(a);if("count"===e.op){var i=(0,me.ZN)(e._result).count,s=i;n.forEach(t=>{var r=t.previousDocumentData&&e.doesDocumentDataMatch(t.previousDocumentData),a=e.doesDocumentDataMatch(t.documentData);!r&&a&&s++,r&&!a&&s--}),s!==i&&(t=!0,e._setResultData(s))}else{var o=tr(e,n);o.runFullQueryAgain?r=!0:o.changed&&(t=!0,e._setResultData(o.newResults))}}}if(r)return e._execOverDatabase().then(r=>{var a=r.result;return e._latestChangeEvent=r.counter,"number"==typeof a?(e._result&&a===e._result.count||(t=!0,e._setResultData(a)),t):(e._result&&function(e,t,r){if(t.length!==r.length)return!1;for(var a=0,n=t.length;a{a++});if(e.isFindOneByIdQuery)if(Array.isArray(e.isFindOneByIdQuery)){var i=e.isFindOneByIdQuery;if(i=i.filter(r=>{var a=e.collection._docCache.getLatestDocumentDataIfExists(r);return!a||(a._deleted||t.push(a),!1)}),i.length>0){var s=await r.storageInstance.findDocumentsById(i,!1);t=t.concat(s)}}else{var o=e.isFindOneByIdQuery,c=e.collection._docCache.getLatestDocumentDataIfExists(o);if(!c){var u=await r.storageInstance.findDocumentsById([o],!1);u[0]&&(c=u[0])}c&&!c._deleted&&t.push(c)}else{var h=e.getPreparedQuery(),l=await r.storageInstance.query(h);t=l.documents}return n.unsubscribe(),a>0?(await(0,ue.ND)(0),gr(e)):{docs:t,counter:r._changeEventBuffer.getCounter()}}var br="collection",wr="storage-token",Dr=q({version:0,title:"RxInternalDocument",primaryKey:{key:"id",fields:["context","key"],separator:"|"},type:"object",properties:{id:{type:"string",maxLength:200},key:{type:"string"},context:{type:"string",enum:[br,wr,"rx-migration-status","rx-pipeline-checkpoint","OTHER"]},data:{type:"object",additionalProperties:!0}},indexes:[],required:["key","context","data"],additionalProperties:!1,sharding:{shards:1,mode:"collection"}});function xr(e,t){return A(Dr,{key:e,context:t})}async function _r(e){var t=Ge(e.schema,{selector:{context:br,_deleted:{$eq:!1}},sort:[{id:"asc"}],skip:0});return(await e.query(t)).documents}var kr="storageToken",Ir=xr(kr,wr);function Er(e,t){return e+"-"+t.version}function Cr(e,t){return t=function(e,t){for(var r=Object.keys(e.defaultValues),a=0;ae.data.name===n),u=[];c.forEach(e=>{u.push({collectionName:e.data.name,schema:e.data.schema,isCollection:!0}),e.data.connectedStorages.forEach(e=>u.push({collectionName:e.collectionName,isCollection:!1,schema:e.schema}))});var h=new Set;if(u=u.filter(e=>{var t=e.collectionName+"||"+e.schema.version;return!h.has(t)&&(h.add(t),!0)}),await Promise.all(u.map(async t=>{var o=await e.createStorageInstance({collectionName:t.collectionName,databaseInstanceToken:r,databaseName:a,multiInstance:i,options:{},schema:t.schema,password:s,devMode:x.isDevMode()});await o.remove(),t.isCollection&&await st("postRemoveRxCollection",{storage:e,databaseName:a,collectionName:n})})),o){var l=c.map(e=>{var t=dt(e);return t._deleted=!0,t._meta.lwt=ie(),t._rev=at(r,e),{previous:e,document:t}});await t.bulkWrite(l,"rx-database-remove-collection-all")}}function Or(e){if(e.closed)throw R("COL21",{collection:e.name,version:e.schema.version})}var Sr=function(){function e(e){this.subs=[],this.counter=0,this.eventCounterMap=new WeakMap,this.buffer=[],this.limit=100,this.tasks=new Set,this.collection=e,this.subs.push(this.collection.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal)).subscribe(e=>{this.tasks.add(()=>this._handleChangeEvents(e.events)),this.tasks.size<=1&&(0,ue.vN)().then(()=>{this.processTasks()})}))}var t=e.prototype;return t.processTasks=function(){0!==this.tasks.size&&(Array.from(this.tasks).forEach(e=>e()),this.tasks.clear())},t._handleChangeEvents=function(e){var t=this.counter;this.counter=this.counter+e.length,e.length>this.limit?this.buffer=e.slice(-1*e.length):(this.buffer=this.buffer.concat(e),this.buffer=this.buffer.slice(-1*this.limit));for(var r=t+1,a=this.eventCounterMap,n=0;nt(e))},t.reduceByLastOfDoc=function(e){return this.processTasks(),e.slice(0)},t.close=function(){this.tasks.clear(),this.subs.forEach(e=>e.unsubscribe())},e}();var jr=new WeakMap;function Pr(e){var t=e.schema.getDocumentPrototype(),r=function(e){var t={};return Object.entries(e.methods).forEach(([e,r])=>{t[e]=r}),t}(e),a={};return[t,r,Kt].forEach(e=>{Object.getOwnPropertyNames(e).forEach(t=>{var r=Object.getOwnPropertyDescriptor(e,t),n=!0;(t.startsWith("_")||t.endsWith("_")||t.startsWith("$")||t.endsWith("$"))&&(n=!1),"function"==typeof r.value?Object.defineProperty(a,t,{get(){return r.value.bind(this)},enumerable:n,configurable:!1}):(r.enumerable=n,r.configurable=!1,r.writable&&(r.writable=!1),Object.defineProperty(a,t,r))})}),a}function $r(e,t,r){var a=function(e,t,r){var a=new e(t,r);return it("createRxDocument",a),a}(t,e,x.deepFreezeWhenDevMode(r));return e._runHooksSync("post","create",r,a),it("postCreateRxDocument",a),a}var Nr={isEqual:(e,t,r)=>(e=Br(e),t=Br(t),(0,St.b)(lt(e),lt(t))),resolve:e=>e.realMasterState};function Br(e){return e._attachments||((e=s(e))._attachments={}),e}var Mr=["pre","post"],Ar=["insert","save","remove","create"],qr=!1,Tr=new Set,Lr=function(){function e(e,t,r,a,n={},i={},s={},o={},c={},u=or,h={},l=Nr){this.storageInstance={},this.timeouts=new Set,this.incrementalWriteQueue={},this.awaitBeforeReads=new Set,this._incrementalUpsertQueues=new Map,this.synced=!1,this.hooks={},this._subs=[],this._docCache={},this._queryCache=new rr,this.$={},this.checkpoint$={},this._changeEventBuffer={},this.eventBulks$={},this.onClose=[],this.closed=!1,this.onRemove=[],this.database=e,this.name=t,this.schema=r,this.internalStorageInstance=a,this.instanceCreationOptions=n,this.migrationStrategies=i,this.methods=s,this.attachments=o,this.options=c,this.cacheReplacementPolicy=u,this.statics=h,this.conflictHandler=l,function(e){if(qr)return;qr=!0;var t=Object.getPrototypeOf(e);Ar.forEach(e=>{Mr.map(r=>{var a=r+(0,N.Z2)(e);t[a]=function(t,a){return this.addHook(r,e,t,a)}})})}(this.asRxCollection),e&&(this.eventBulks$=e.eventBulks$.pipe((0,Nt.p)(e=>e.collectionName===this.name))),this.database&&Tr.add(this)}var t=e.prototype;return t.prepare=async function(){if(!await de()){for(var e=0;e<10&&Tr.size>16;)e++,await this.promiseWait(30);if(Tr.size>16)throw R("COL23",{database:this.database.name,collection:this.name,args:{existing:Array.from(Tr.values()).map(e=>({db:e.database?e.database.name:"",c:e.name}))}})}var t,r;this.storageInstance=mt(this.database,this.internalStorageInstance,this.schema.jsonSchema),this.incrementalWriteQueue=new Ft(this.storageInstance,this.schema.primaryPath,(e,t)=>Zt(this,e,t),e=>this._runHooks("post","save",e)),this.$=this.eventBulks$.pipe((0,Gt.Z)(e=>Wt(e))),this.checkpoint$=this.eventBulks$.pipe((0,$t.T)(e=>e.checkpoint)),this._changeEventBuffer=(t=this.asRxCollection,new Sr(t)),this._docCache=new ur(this.schema.primaryPath,this.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal),(0,$t.T)(e=>e.events)),e=>{var t;return r||(t=this.asRxCollection,r=i(jr,t,()=>zt(Pr(t)))),$r(this.asRxCollection,r,e)});var a=this.database.internalStore.changeStream().pipe((0,Nt.p)(e=>{var t=this.name+"-"+this.schema.version;return!!e.events.find(e=>"collection"===e.documentData.context&&e.documentData.key===t&&"DELETE"===e.operation)})).subscribe(async()=>{await this.close(),await Promise.all(this.onRemove.map(e=>e()))});this._subs.push(a);var n=await this.database.storageToken,s=this.storageInstance.changeStream().subscribe(e=>{var t={id:e.id,isLocal:!1,internal:!1,collectionName:this.name,storageToken:n,events:e.events,databaseToken:this.database.token,checkpoint:e.checkpoint,context:e.context};this.database.$emit(t)});return this._subs.push(s),ue.em},t.cleanup=function(e){throw Or(this),qt("cleanup")},t.migrationNeeded=function(){throw qt("migration-schema")},t.getMigrationState=function(){throw qt("migration-schema")},t.startMigration=function(e=10){return Or(this),this.getMigrationState().startMigration(e)},t.migratePromise=function(e=10){return this.getMigrationState().migratePromise(e)},t.insert=async function(e){Or(this);var t=await this.bulkInsert([e]),r=t.error[0];return ut(this,e[this.schema.primaryPath],e,r),(0,me.ZN)(t.success[0])},t.insertIfNotExists=async function(e){var t=await this.bulkInsert([e]);if(t.error.length>0){var r=t.error[0];if(409===r.status){var a=r.documentInDb;return lr(this._docCache,[a])[0]}throw r}return t.success[0]},t.bulkInsert=async function(e){if(Or(this),0===e.length)return{success:[],error:[]};var t,r=this.schema.primaryPath,a=new Set;if(this.hasHooks("pre","insert"))t=await Promise.all(e.map(e=>{var t=Cr(this.schema,e);return this._runHooks("pre","insert",t).then(()=>(a.add(t[r]),{document:t}))}));else{t=new Array(e.length);for(var n=this.schema,i=0;i{var t=e.document;l.set(t[r],t)}),await Promise.all(h.success.map(e=>this._runHooks("post","insert",l.get(e.primary),e)))}return h},t.bulkRemove=async function(e){Or(this);var t,r=this.schema.primaryPath;if(0===e.length)return{success:[],error:[]};"string"==typeof e[0]?t=await this.findByIds(e).exec():(t=new Map,e.forEach(e=>t.set(e.primary,e)));var a=[],n=new Map;Array.from(t.values()).forEach(e=>{var t=e.toMutableJSON(!0);a.push(t),n.set(e.primary,t)}),await Promise.all(a.map(e=>{var r=e[this.schema.primaryPath];return this._runHooks("pre","remove",e,t.get(r))}));var i=a.map(e=>{var t=s(e);return t._deleted=!0,{previous:e,document:t}}),o=await this.storageInstance.bulkWrite(i,"rx-collection-bulk-remove"),c=vt(this.schema.primaryPath,i,o),u=[],h=c.map(e=>{var t=e[r],a=this._docCache.getCachedRxDocument(e);return u.push(a),t});return await Promise.all(h.map(e=>this._runHooks("post","remove",n.get(e),t.get(e)))),{success:u,error:o.error}},t.bulkUpsert=async function(e){Or(this);var t=[],r=new Map;e.forEach(e=>{var a=Cr(this.schema,e),n=a[this.schema.primaryPath];if(!n)throw R("COL3",{primaryPath:this.schema.primaryPath,data:a,schema:this.schema.jsonSchema});r.set(n,a),t.push(a)});var a=await this.bulkInsert(t),i=a.success.slice(0),s=[];return await Promise.all(a.error.map(async e=>{if(409!==e.status)s.push(e);else{var t=e.documentId,a=n(r,t),o=(0,me.ZN)(e.documentInDb),c=this._docCache.getCachedRxDocuments([o])[0],u=await c.incrementalModify(()=>a);i.push(u)}})),{error:s,success:i}},t.upsert=async function(e){Or(this);var t=await this.bulkUpsert([e]);return ut(this.asRxCollection,e[this.schema.primaryPath],e,t.error[0]),t.success[0]},t.incrementalUpsert=function(e){Or(this);var t=Cr(this.schema,e),r=t[this.schema.primaryPath];if(!r)throw R("COL4",{data:e});var a=this._incrementalUpsertQueues.get(r);return a||(a=ue.em),a=a.then(()=>function(e,t,r){var a=e._docCache.getLatestDocumentDataIfExists(t);if(a)return Promise.resolve({doc:e._docCache.getCachedRxDocuments([a])[0],inserted:!1});return e.findOne(t).exec().then(t=>t?{doc:t,inserted:!1}:e.insert(r).then(e=>({doc:e,inserted:!0})))}(this,r,t)).then(e=>e.inserted?e.doc:function(e,t){return e.incrementalModify(e=>t)}(e.doc,t)),this._incrementalUpsertQueues.set(r,a),a},t.find=function(e){return Or(this),it("prePrepareRxQuery",{op:"find",queryObj:e,collection:this}),e||(e={selector:{}}),vr("find",e,this)},t.findOne=function(e){var t;if(Or(this),it("prePrepareRxQuery",{op:"findOne",queryObj:e,collection:this}),"string"==typeof e)t=vr("findOne",{selector:{[this.schema.primaryPath]:e},limit:1},this);else{if(e||(e={selector:{}}),e.limit)throw R("QU6");(e=s(e)).limit=1,t=vr("findOne",e,this)}return t},t.count=function(e){return Or(this),e||(e={selector:{}}),vr("count",e,this)},t.findByIds=function(e){return Or(this),vr("findByIds",{selector:{[this.schema.primaryPath]:{$in:e.slice(0)}}},this)},t.exportJSON=function(){throw qt("json-dump")},t.importJSON=function(e){throw qt("json-dump")},t.insertCRDT=function(e){throw qt("crdt")},t.addPipeline=function(e){throw qt("pipeline")},t.addHook=function(e,t,r,a=!1){if("function"!=typeof r)throw O("COL7",{key:t,when:e});if(!Mr.includes(e))throw O("COL8",{key:t,when:e});if(!Ar.includes(t))throw R("COL9",{key:t});if("post"===e&&"create"===t&&!0===a)throw R("COL10",{when:e,key:t,parallel:a});var n=r.bind(this),i=a?"parallel":"series";this.hooks[t]=this.hooks[t]||{},this.hooks[t][e]=this.hooks[t][e]||{series:[],parallel:[]},this.hooks[t][e][i].push(n)},t.getHooks=function(e,t){return this.hooks[t]&&this.hooks[t][e]?this.hooks[t][e]:{series:[],parallel:[]}},t.hasHooks=function(e,t){if(!this.hooks[t]||!this.hooks[t][e])return!1;var r=this.getHooks(e,t);return!!r&&(r.series.length>0||r.parallel.length>0)},t._runHooks=function(e,t,r,a){var n=this.getHooks(e,t);if(!n)return ue.em;var i=n.series.map(e=>()=>e(r,a));return(0,ue.h$)(i).then(()=>Promise.all(n.parallel.map(e=>e(r,a))))},t._runHooksSync=function(e,t,r,a){if(this.hasHooks(e,t)){var n=this.getHooks(e,t);n&&n.series.forEach(e=>e(r,a))}},t.promiseWait=function(e){return new Promise(t=>{var r=setTimeout(()=>{this.timeouts.delete(r),t()},e);this.timeouts.add(r)})},t.close=async function(){return this.closed?ue.Dr:(Tr.delete(this),await Promise.all(this.onClose.map(e=>e())),this.closed=!0,Array.from(this.timeouts).forEach(e=>clearTimeout(e)),this._changeEventBuffer&&this._changeEventBuffer.close(),this.database.requestIdlePromise().then(()=>this.storageInstance.close()).then(()=>(this._subs.forEach(e=>e.unsubscribe()),delete this.database.collections[this.name],this.database.collectionsSubject$.next({collection:this.asRxCollection,type:"CLOSED"}),st("postCloseRxCollection",this).then(()=>!0))))},t.remove=async function(){await this.close(),await Promise.all(this.onRemove.map(e=>e())),await Rr(this.database.storage,this.database.internalStore,this.database.token,this.database.name,this.name,this.database.multiInstance,this.database.password,this.database.hashFunction)},(0,b.A)(e,[{key:"insert$",get:function(){return this.$.pipe((0,Nt.p)(e=>"INSERT"===e.operation))}},{key:"update$",get:function(){return this.$.pipe((0,Nt.p)(e=>"UPDATE"===e.operation))}},{key:"remove$",get:function(){return this.$.pipe((0,Nt.p)(e=>"DELETE"===e.operation))}},{key:"asRxCollection",get:function(){return this}}])}();var Qr=r(7635),Wr=r(5525),Fr=new Set,Hr=new Map,Kr=function(){function e(e,t,r,a,n,i,s=!1,o={},c,u,h,l,d,m){this.idleQueue=new Qr.G,this.rxdbVersion=Ct,this.storageInstances=new Set,this._subs=[],this.startupErrors=[],this.onClose=[],this.closed=!1,this.collections={},this.states={},this.eventBulks$=new ae.B,this.closePromise=null,this.collectionsSubject$=new ae.B,this.observable$=this.eventBulks$.pipe((0,Gt.Z)(e=>Wt(e))),this.storageToken=ue.Dr,this.storageTokenDocument=ue.Dr,this.emittedEventBulkIds=new Wr.p4(6e4),this.name=e,this.token=t,this.storage=r,this.instanceCreationOptions=a,this.password=n,this.multiInstance=i,this.eventReduce=s,this.options=o,this.internalStore=c,this.hashFunction=u,this.cleanupPolicy=h,this.allowSlowCount=l,this.reactivity=d,this.onClosed=m,"pseudoInstance"!==this.name&&(this.internalStore=mt(this.asRxDatabase,c,Dr),this.storageTokenDocument=async function(e){var t=(0,N.zs)(10),r=e.password?await e.hashFunction(JSON.stringify(e.password)):void 0,a=[{document:{id:Ir,context:wr,key:kr,data:{rxdbVersion:e.rxdbVersion,token:t,instanceToken:e.token,passwordHash:r},_deleted:!1,_meta:{lwt:1},_rev:"",_attachments:{}}}],n=await e.internalStore.bulkWrite(a,"internal-add-storage-token");if(!n.error[0])return vt("id",a,n)[0];var i=(0,me.ZN)(n.error[0]);if(i.isError&&S(i)){var s=i;if(!function(e,t){if(!e)return!1;var r=e.split(".")[0],a=t.split(".")[0];return"16"===r&&"17"===a||r===a}(s.documentInDb.data.rxdbVersion,e.rxdbVersion))throw R("DM5",{args:{database:e.name,databaseStateVersion:s.documentInDb.data.rxdbVersion,codeVersion:e.rxdbVersion}});if(r&&r!==s.documentInDb.data.passwordHash)throw R("DB1",{passwordHash:r,existingPasswordHash:s.documentInDb.data.passwordHash});var o=s.documentInDb;return(0,me.ZN)(o)}throw i}(this.asRxDatabase).catch(e=>this.startupErrors.push(e)),this.storageToken=this.storageTokenDocument.then(e=>e.data.token).catch(e=>this.startupErrors.push(e)))}var t=e.prototype;return t.getReactivityFactory=function(){if(!this.reactivity)throw R("DB14",{database:this.name});return this.reactivity},t[Symbol.asyncDispose]=async function(){await this.close()},t.$emit=function(e){this.emittedEventBulkIds.has(e.id)||(this.emittedEventBulkIds.add(e.id),this.eventBulks$.next(e))},t.removeCollectionDoc=async function(e,t){var r=await ot(this.internalStore,xr(Er(e,t),br));if(!r)throw R("SNH",{name:e,schema:t});var a=dt(r);a._deleted=!0,await this.internalStore.bulkWrite([{document:a,previous:r}],"rx-database-remove-collection")},t.addCollections=async function(e){var t={},r={},a=[],n={};await Promise.all(Object.entries(e).map(async([e,i])=>{var o=e,c=i.schema;t[o]=c;var u=Pt(c,this.hashFunction);if(r[o]=u,this.collections[e])throw R("DB3",{name:e});var h=Er(e,c),l={id:xr(h,br),key:h,context:br,data:{name:o,schemaHash:await u.hash,schema:u.jsonSchema,version:u.version,connectedStorages:[]},_deleted:!1,_meta:{lwt:1},_rev:"",_attachments:{}};a.push({document:l});var d=Object.assign({},i,{name:o,schema:u,database:this}),m=s(i);m.database=this,m.name=e,it("preCreateRxCollection",m),d.conflictHandler=m.conflictHandler,n[o]=d}));var i=await this.internalStore.bulkWrite(a,"rx-database-add-collection");await async function(e){if(await e.storageToken,e.startupErrors[0])throw e.startupErrors[0]}(this),await Promise.all(i.error.map(async e=>{if(409!==e.status)throw R("DB12",{database:this.name,writeError:e});var a=(0,me.ZN)(e.documentInDb),n=a.data.name,i=r[n];if(a.data.schemaHash!==await i.hash)throw R("DB6",{database:this.name,collection:n,previousSchemaHash:a.data.schemaHash,schemaHash:await i.hash,previousSchema:a.data.schema,schema:(0,me.ZN)(t[n])})}));var o={};return await Promise.all(Object.keys(e).map(async e=>{var t=n[e],r=await async function({database:e,name:t,schema:r,instanceCreationOptions:a={},migrationStrategies:n={},autoMigrate:i=!0,statics:s={},methods:o={},attachments:c={},options:u={},localDocuments:h=!1,cacheReplacementPolicy:l=or,conflictHandler:d=Nr}){var m={databaseInstanceToken:e.token,databaseName:e.name,collectionName:t,schema:r.jsonSchema,options:a,multiInstance:e.multiInstance,password:e.password,devMode:x.isDevMode()};it("preCreateRxStorageInstance",m);var f=await async function(e,t){return t.multiInstance=e.multiInstance,await e.storage.createStorageInstance(t)}(e,m),p=new Lr(e,t,r,f,a,n,o,c,u,l,s,d);try{await p.prepare(),Object.entries(s).forEach(([e,t])=>{Object.defineProperty(p,e,{get:()=>t.bind(p)})}),it("createRxCollection",{collection:p,creator:{name:t,schema:r,storageInstance:f,instanceCreationOptions:a,migrationStrategies:n,methods:o,attachments:c,options:u,cacheReplacementPolicy:l,localDocuments:h,statics:s}}),i&&0!==p.schema.version&&await p.migratePromise()}catch(v){throw Tr.delete(p),await f.close(),v}return p}(t);o[e]=r,this.collections[e]=r,this.collectionsSubject$.next({collection:r,type:"ADDED"}),this[e]||Object.defineProperty(this,e,{get:()=>this.collections[e]})})),o},t.lockedRun=function(e){return this.idleQueue.wrapCall(e)},t.requestIdlePromise=function(){return this.idleQueue.requestIdlePromise()},t.exportJSON=function(e){throw qt("json-dump")},t.addState=function(e){throw qt("state")},t.importJSON=function(e){throw qt("json-dump")},t.backup=function(e){throw qt("backup")},t.leaderElector=function(){throw qt("leader-election")},t.isLeader=function(){throw qt("leader-election")},t.waitForLeadership=function(){throw qt("leader-election")},t.migrationStates=function(){throw qt("migration-schema")},t.close=function(){if(this.closePromise)return this.closePromise;var{promise:e,resolve:t}=zr(),r=e=>{this.onClosed&&this.onClosed(),this.closed=!0,t(e)};return this.closePromise=e,(async()=>{if(await st("preCloseRxDatabase",this),this.eventBulks$.complete(),this.collectionsSubject$.complete(),this._subs.map(e=>e.unsubscribe()),"pseudoInstance"!==this.name)return this.requestIdlePromise().then(()=>Promise.all(this.onClose.map(e=>e()))).then(()=>Promise.all(Object.keys(this.collections).map(e=>this.collections[e]).map(e=>e.close()))).then(()=>this.internalStore.close()).then(()=>r(!0));r(!1)})(),e},t.remove=function(){return this.close().then(()=>async function(e,t,r=!0,a){var n=(0,N.zs)(10),i=await Ur(n,t,e,{},r,a),s=await _r(i),o=new Set;s.forEach(e=>o.add(e.data.name));var c=Array.from(o);return await Promise.all(c.map(s=>Rr(t,i,n,e,s,r,a))),await st("postRemoveRxDatabase",{databaseName:e,storage:t}),await i.remove(),c}(this.name,this.storage,this.multiInstance,this.password))},(0,b.A)(e,[{key:"$",get:function(){return this.observable$}},{key:"collections$",get:function(){return this.collectionsSubject$.asObservable()}},{key:"asRxDatabase",get:function(){return this}}])}();function zr(){var e,t;return{promise:new Promise((r,a)=>{e=r,t=a}),resolve:e,reject:t}}function Zr(e,t){return t.name+"|"+e}async function Ur(e,t,r,a,n,i){return await t.createStorageInstance({databaseInstanceToken:e,databaseName:r,collectionName:"_rxdb_internal",schema:Dr,options:a,multiInstance:n,password:i,devMode:x.isDevMode()})}function Vr({storage:e,instanceCreationOptions:t,name:r,password:a,multiInstance:n=!0,eventReduce:i=!0,ignoreDuplicate:s=!1,options:o={},cleanupPolicy:c,closeDuplicates:u=!1,allowSlowCount:h=!1,localDocuments:l=!1,hashFunction:d=ce,reactivity:m}){it("preCreateRxDatabase",{storage:e,instanceCreationOptions:t,name:r,password:a,multiInstance:n,eventReduce:i,ignoreDuplicate:s,options:o,localDocuments:l});var f=Zr(r,e),p=Hr.get(f)||new Set,v=zr(),y=Array.from(p),g=()=>{p.delete(v.promise),Fr.delete(f)};return p.add(v.promise),Hr.set(f,p),(async()=>{if(u&&await Promise.all(y.map(e=>e.catch(()=>null).then(e=>e&&e.close()))),s){if(!x.isDevMode())throw R("DB9",{database:r})}else!function(e,t){if(Fr.has(Zr(e,t)))throw R("DB8",{name:e,storage:t.name,link:"https://rxdb.info/rx-database.html#ignoreduplicate"})}(r,e);Fr.add(f);var p=(0,N.zs)(10),v=await Ur(p,e,r,t,n,a),b=new Kr(r,p,e,t,a,n,i,o,v,d,c,h,m,g);return await st("createRxDatabase",{database:b,creator:{storage:e,instanceCreationOptions:t,name:r,password:a,multiInstance:n,eventReduce:i,ignoreDuplicate:s,options:o,localDocuments:l}}),b})().then(e=>{v.resolve(e)}).catch(e=>{v.reject(e),g()}),v.promise}var Jr={RxSchema:jt.prototype,RxDocument:Kt,RxQuery:pr.prototype,RxCollection:Lr.prototype,RxDatabase:Kr.prototype},Gr=new Set,Xr=new Set;var Yr=new WeakMap,ea=new WeakMap;function ta(e){var t=Yr.get(e);if(!t){var r=e.database?e.database:e,a=e.database?e.name:"";throw R("LD8",{database:r.name,collection:a})}return t}function ra(e,t,r,a,n,i){return t.createStorageInstance({databaseInstanceToken:e,databaseName:r,collectionName:ia(a),schema:sa,options:n,multiInstance:i,devMode:x.isDevMode()})}function aa(e){var t=Yr.get(e);if(t)return Yr.delete(e),t.then(e=>e.storageInstance.close())}async function na(e,t,r){var a=(0,N.zs)(10),n=await ra(a,e,t,r,{},!1);await n.remove()}function ia(e){return"plugin-local-documents-"+e}var sa=q({title:"RxLocalDocument",version:0,primaryKey:"id",type:"object",properties:{id:{type:"string",maxLength:128},data:{type:"object",additionalProperties:!0}},required:["id","data"]});async function oa(e,t){var r=await ta(this),a={id:e,data:t,_deleted:!1,_meta:{lwt:1},_rev:"",_attachments:{}};return ct(r.storageInstance,{document:a},"local-document-insert").then(e=>r.docCache.getCachedRxDocument(e))}function ca(e,t){return this.getLocal(e).then(r=>r?r.incrementalModify(()=>t):this.insertLocal(e,t))}async function ua(e){var t=await ta(this),r=t.docCache,a=r.getLatestDocumentDataIfExists(e);return a?Promise.resolve(r.getCachedRxDocument(a)):ot(t.storageInstance,e).then(e=>e?t.docCache.getCachedRxDocument(e):null)}function ha(e){return this.$.pipe((0,Bt.Z)(null),(0,Gt.Z)(async t=>t?{changeEvent:t}:{doc:await this.getLocal(e)}),(0,Gt.Z)(async t=>{if(t.changeEvent){var r=t.changeEvent;return r.isLocal&&r.documentId===e?{use:!0,doc:await this.getLocal(e)}:{use:!1}}return{use:!0,doc:t.doc}}),(0,Nt.p)(e=>e.use),(0,$t.T)(e=>e.doc))}var la=function(e){function t(t,r,a){var n;return(n=e.call(this,null,r)||this).id=t,n.parent=a,n}return(0,w.A)(t,e),t}(zt()),da={get isLocal(){return!0},get allAttachments$(){throw R("LD1",{document:this})},get primaryPath(){return"id"},get primary(){return this.id},get $(){var e=n(ea,this.parent),t=this.primary;return this.parent.eventBulks$.pipe((0,Nt.p)(e=>!!e.isLocal),(0,$t.T)(e=>e.events.find(e=>e.documentId===t)),(0,Nt.p)(e=>!!e),(0,$t.T)(e=>Tt((0,me.ZN)(e))),(0,Bt.Z)(e.docCache.getLatestDocumentData(this.primary)),(0,Mt.F)((e,t)=>e._rev===t._rev),(0,$t.T)(t=>e.docCache.getCachedRxDocument(t)),(0,At.t)(me.bz))},get $$(){var e=this,t=pa(e);return t.getReactivityFactory().fromObservable(e.$,e.getLatest()._data,t)},get deleted$$(){var e=this,t=pa(e);return t.getReactivityFactory().fromObservable(e.deleted$,e.getLatest().deleted,t)},getLatest(){var e=n(ea,this.parent),t=e.docCache.getLatestDocumentData(this.primary);return e.docCache.getCachedRxDocument(t)},get(e){if(e="data."+e,this._data){if("string"!=typeof e)throw O("LD2",{objPath:e});var t=v(this._data,e);return t=x.deepFreezeWhenDevMode(t)}},get$(e){if(e="data."+e,x.isDevMode()){if(e.includes(".item."))throw R("LD3",{objPath:e});if(e===this.primaryPath)throw R("LD4")}return this.$.pipe((0,$t.T)(e=>e._data),(0,$t.T)(t=>v(t,e)),(0,Mt.F)())},get$$(e){var t=pa(this);return t.getReactivityFactory().fromObservable(this.get$(e),this.getLatest().get(e),t)},async incrementalModify(e){var t=await ta(this.parent);return t.incrementalWriteQueue.addWrite(this._data,async t=>(t.data=await e(t.data,this),t)).then(e=>t.docCache.getCachedRxDocument(e))},incrementalPatch(e){return this.incrementalModify(t=>(Object.entries(e).forEach(([e,r])=>{t[e]=r}),t))},async _saveData(e){var t=await ta(this.parent),r=this._data;e.id=this.id;var a=[{previous:r,document:e}];return t.storageInstance.bulkWrite(a,"local-document-save-data").then(t=>{if(t.error[0])throw t.error[0];var r=vt(this.collection.schema.primaryPath,a,t)[0];(e=s(e))._rev=r._rev})},async remove(){var e=await ta(this.parent),t=s(this._data);return t._deleted=!0,ct(e.storageInstance,{previous:this._data,document:t},"local-document-remove").then(t=>e.docCache.getCachedRxDocument(t))}},ma=!1,fa=()=>{if(!ma){ma=!0;var e=Kt;Object.getOwnPropertyNames(e).forEach(t=>{if(!Object.getOwnPropertyDescriptor(da,t)){var r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(da,t,r)}});["populate","update","putAttachment","putAttachmentBase64","getAttachment","allAttachments"].forEach(e=>da[e]=(e=>()=>{throw R("LD6",{functionName:e})})(e))}};function pa(e){var t=e.parent;return t instanceof Kr?t:t.database}function va(e){var t=e.database?e.database:e,r=e.database?e.name:"",a=(async()=>{var a=await ra(t.token,t.storage,t.name,r,t.instanceCreationOptions,t.multiInstance);a=mt(t,a,sa);var n=new ur("id",t.eventBulks$.pipe((0,Nt.p)(e=>{var t=!1;return(""===r&&!e.collectionName||""!==r&&e.collectionName===r)&&(t=!0),t&&e.isLocal}),(0,$t.T)(e=>e.events)),t=>function(e,t){fa();var r=new la(e.id,e,t);return Object.setPrototypeOf(r,da),r.prototype=da,r}(t,e)),i=new Ft(a,"id",()=>{},()=>{}),s=await t.storageToken,o=a.changeStream().subscribe(r=>{for(var a=new Array(r.events.length),n=r.events,i=e.database?e.name:void 0,o=0;o{e.insertLocal=oa,e.upsertLocal=ca,e.getLocal=ua,e.getLocal$=ha},RxDatabase:e=>{e.insertLocal=oa,e.upsertLocal=ca,e.getLocal=ua,e.getLocal$=ha}},hooks:{createRxDatabase:{before:e=>{e.creator.localDocuments&&va(e.database)}},createRxCollection:{before:e=>{e.creator.localDocuments&&va(e.collection)}},preCloseRxDatabase:{after:e=>aa(e)},postCloseRxCollection:{after:e=>aa(e)},postRemoveRxDatabase:{after:e=>na(e.storage,e.databaseName,"")},postRemoveRxCollection:{after:e=>na(e.storage,e.databaseName,e.collectionName)}},overwritable:{}};let ga;function ba(){return"undefined"!=typeof window&&window.indexedDB}function wa(){return ga||(ga=(async()=>{!function(e){if(it("preAddRxPlugin",{plugin:e,plugins:Gr}),!Gr.has(e)){if(Xr.has(e.name))throw R("PL3",{name:e.name,plugin:e});if(Gr.add(e),Xr.add(e.name),!e.rxdb)throw O("PL1",{plugin:e});e.init&&e.init(),e.prototypes&&Object.entries(e.prototypes).forEach(([e,t])=>t(Jr[e])),e.overwritable&&Object.assign(x,e.overwritable),e.hooks&&Object.entries(e.hooks).forEach(([e,t])=>{t.after&&nt[e].push(t.after),t.before&&nt[e].unshift(t.before)})}}(ya);return await Vr({name:"rxdb-landing-v4",localDocuments:!0,storage:Ot()})})()),ga}},8342(e,t,r){r.d(t,{L$:()=>s,Z2:()=>i,zs:()=>n});var a="abcdefghijklmnopqrstuvwxyz";function n(e=10){for(var t="",r=0;rl,contentTitle:()=>i,default:()=>h,frontMatter:()=>r,metadata:()=>t,toc:()=>c});const t=JSON.parse('{"id":"releases/8.0.0","title":"Meet RxDB 8.0.0 - New Defaults & Performance","description":"Discover how RxDB 8.0.0 boosts performance, simplifies schema handling, and streamlines reactive data updates for modern apps.","source":"@site/docs/releases/8.0.0.md","sourceDirName":"releases","slug":"/releases/8.0.0.html","permalink":"/releases/8.0.0.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Meet RxDB 8.0.0 - New Defaults & Performance","slug":"8.0.0.html","description":"Discover how RxDB 8.0.0 boosts performance, simplifies schema handling, and streamlines reactive data updates for modern apps."},"sidebar":"tutorialSidebar","previous":{"title":"9.0.0","permalink":"/releases/9.0.0.html"},"next":{"title":"Benefits of RxDB & Browser Databases","permalink":"/articles/browser-database.html"}}');var a=s(4848),o=s(8453);const r={title:"Meet RxDB 8.0.0 - New Defaults & Performance",slug:"8.0.0.html",description:"Discover how RxDB 8.0.0 boosts performance, simplifies schema handling, and streamlines reactive data updates for modern apps."},i="8.0.0",l={},c=[{value:"disableKeyCompression by default",id:"disablekeycompression-by-default",level:2},{value:"collection() now only accepts a RxJsonSchema as schema",id:"collection-now-only-accepts-a-rxjsonschema-as-schema",level:2},{value:"required fields have to be set via array",id:"required-fields-have-to-be-set-via-array",level:2},{value:"Setters are only callable on temporary documents",id:"setters-are-only-callable-on-temporary-documents",level:2},{value:"middleware-hooks contain plain json as first parameter and RxDocument as second",id:"middleware-hooks-contain-plain-json-as-first-parameter-and-rxdocument-as-second",level:2},{value:"multiInstance is now done via broadcast-channel",id:"multiinstance-is-now-done-via-broadcast-channel",level:2},{value:"Set QueryChangeDetection via RxDatabase-option",id:"set-querychangedetection-via-rxdatabase-option",level:2},{value:"Reuse an RxDocument-prototype per collection instead of adding getters/setters to each document",id:"reuse-an-rxdocument-prototype-per-collection-instead-of-adding-getterssetters-to-each-document",level:2},{value:"Rewritten the inMemory-plugin",id:"rewritten-the-inmemory-plugin",level:2},{value:"Some comparisons",id:"some-comparisons",level:2}];function d(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"800",children:"8.0.0"})}),"\n",(0,a.jsxs)(n.p,{children:["When I created RxDB, two years ago, there where some things that I did not consider and other things that I just decided wrong.\nWith the breaking version ",(0,a.jsx)(n.code,{children:"8.0.0"})," I rewrote some parts of RxDB and changed the API a bit. The focus laid on ",(0,a.jsx)(n.strong,{children:"better defaults"})," and ",(0,a.jsx)(n.strong,{children:"better performance"}),"."]}),"\n",(0,a.jsx)(n.h2,{id:"disablekeycompression-by-default",children:"disableKeyCompression by default"}),"\n",(0,a.jsxs)(n.p,{children:["Because the keyCompression was confusing and most users do not use it, it is now disabled by default. Also the naming was bad, so ",(0,a.jsx)(n.code,{children:"disableKeyCompression"})," is now renamed to ",(0,a.jsx)(n.code,{children:"keyCompression"})," which defaults to ",(0,a.jsx)(n.code,{children:"false"}),"."]}),"\n",(0,a.jsx)(n.h2,{id:"collection-now-only-accepts-a-rxjsonschema-as-schema",children:"collection() now only accepts a RxJsonSchema as schema"}),"\n",(0,a.jsxs)(n.p,{children:["In the past, it was allowed to set an ",(0,a.jsx)(n.code,{children:"RxSchema"})," or an ",(0,a.jsx)(n.code,{children:"RxJsonSchema"})," as ",(0,a.jsx)(n.code,{children:"schema"}),"-field when creating a collection. This was confusing and so it is now only allowed to use ",(0,a.jsx)(n.code,{children:"RxJsonSchema"}),"."]}),"\n",(0,a.jsx)(n.h2,{id:"required-fields-have-to-be-set-via-array",children:"required fields have to be set via array"}),"\n",(0,a.jsxs)(n.p,{children:["In the past it was allowed to set a field as required by setting the boolean value ",(0,a.jsx)(n.code,{children:"required: true"}),".\nThis is against the ",(0,a.jsx)(n.a,{href:"https://json-schema.org/understanding-json-schema/reference/object.html#required-properties",children:"json-schema-standard"})," and will make problems when switching between different schema-validation-plugins. Therefore required fields must now be set via ",(0,a.jsx)(n.code,{children:"required: ['fieldOne', 'fieldTwo']"}),"."]}),"\n",(0,a.jsx)(n.h2,{id:"setters-are-only-callable-on-temporary-documents",children:"Setters are only callable on temporary documents"}),"\n",(0,a.jsxs)(n.p,{children:["To be similar to mongoosejs, there was the possibility to set a documents value via ",(0,a.jsx)(n.code,{children:"myDoc.foo = 'bar'"})," and later call ",(0,a.jsx)(n.code,{children:"myDoc.save()"})," to persist these changes.\nBut there was the problem that we have no weak pointers in javascript and therefore we have to store all document-instances in a cache to run change-events on them and make them 'reactive'. To save memory-space, we reuse the same documents when they are used multiple times. This made it hard to determine what happens when multiple parts of an application used the setters at the same time. Also there was an undefined behavior what should happen when a field is changed via setter and also via replication before ",(0,a.jsx)(n.code,{children:"save()"})," was called."]}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.code,{children:"myDoc.age = 50;"}),", ",(0,a.jsx)(n.code,{children:"myDoc.set('age', 50);"})," and ",(0,a.jsx)(n.code,{children:"myDoc.save()"})," is no more allowed on non-temporary-documents"]}),"\n",(0,a.jsxs)(n.li,{children:["Instead, to change document-data, use ",(0,a.jsx)(n.code,{children:"RxDocument.atomicUpdate()"})," or ",(0,a.jsx)(n.code,{children:"RxDocument.atomicSet()"})," or ",(0,a.jsx)(n.code,{children:"RxDocument.update()"}),"."]}),"\n",(0,a.jsxs)(n.li,{children:["The following document-methods no longer exist: ",(0,a.jsx)(n.code,{children:"synced$"}),", ",(0,a.jsx)(n.code,{children:"resync()"})]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"middleware-hooks-contain-plain-json-as-first-parameter-and-rxdocument-as-second",children:"middleware-hooks contain plain json as first parameter and RxDocument as second"}),"\n",(0,a.jsxs)(n.p,{children:["When the middleware-hooks where created, the goal was to work equal then ",(0,a.jsx)(n.a,{href:"http://mongoosejs.com/docs/middleware.html",children:"mongoose"}),". But this is not possible because mongoose is more 'static' and its documents never change their attributes. RxDB is a reactive database and when the state on disc changes, the attributes of the document also change. This caused some undefined behavior especially with async middleware-hooks. To solve this, hooks now can only modify the plain data, not the ",(0,a.jsx)(n.code,{children:"RxDocument"})," itself."]}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".preSave"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(data"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" rxDocument) {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // to set age to 50 before saving, change the first parameter"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" data"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".age "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,a.jsx)(n.h2,{id:"multiinstance-is-now-done-via-broadcast-channel",children:"multiInstance is now done via broadcast-channel"}),"\n",(0,a.jsxs)(n.p,{children:["Because the ",(0,a.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API",children:"BroadcastChannel-API"})," is not usable in all browsers and also not in NodeJs, multiInstance-communication was hard. The hacky workaround was to use a pseudo-socket and regularly check if an other instance has emitted a ",(0,a.jsx)(n.code,{children:"RxChangeEvent"}),".\nThis was expensive because even when the database did nothing, we wasted disk-IO and CPU by handling this."]}),"\n",(0,a.jsxs)(n.p,{children:["To solve this waste, I spend one month creating ",(0,a.jsx)(n.a,{href:"https://github.com/pubkey/broadcast-channel",children:"a module that polyfills the broadcast-channel-api"})," so it works on old browsers, new browsers and even NodeJs. This does not only waste less resources but also has a lower latency. Also the module is used by more projects than RxDB which allows use the fix bugs and improve performance together."]}),"\n",(0,a.jsx)(n.h2,{id:"set-querychangedetection-via-rxdatabase-option",children:"Set QueryChangeDetection via RxDatabase-option"}),"\n",(0,a.jsxs)(n.p,{children:["In the past, the QueryChangeDetection had to be enabled by importing the QueryChangeDetection and calling a function on it. This was strange and also did not allow to toggle the QueryChangeDetection on specific databases.\nNow we set the QueryChangeDetection by adding the boolean field ",(0,a.jsx)(n.code,{children:"queryChangeDetection: true"})," when creating the database."]}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" RxDB"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".create"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" adapter"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'idb'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" queryChangeDetection"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- queryChangeDetection (optional, default: false)"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(db);"})]})]})})}),"\n",(0,a.jsx)(n.h2,{id:"reuse-an-rxdocument-prototype-per-collection-instead-of-adding-getterssetters-to-each-document",children:"Reuse an RxDocument-prototype per collection instead of adding getters/setters to each document"}),"\n",(0,a.jsxs)(n.p,{children:["Because the fields of an ",(0,a.jsx)(n.code,{children:"RxDocument"})," are defined dynamically by the schema and we could not use the ",(0,a.jsx)(n.code,{children:"Proxy"}),"-Object because it is not supported in IE11, there was one workaround used: Each time a document is created, all getters and setters where applied on it. This was expensive and now we use a different approach.\nOnce per collection, a custom RxDocument-prototype and constructor is created and each RxDocument of this collection is created with the constructor. This change is a rewrite internally and should not change anything for RxDB-users. If you use RxDB with vuejs, you might get some problems that can be fixed by upgrading vuejs to the latest version."]}),"\n",(0,a.jsx)(n.h2,{id:"rewritten-the-inmemory-plugin",children:"Rewritten the inMemory-plugin"}),"\n",(0,a.jsxs)(n.p,{children:["The inMemory-plugin was written with some wrong estimations. I rewrote it and added much more tests. Also ",(0,a.jsx)(n.code,{children:"awaitPersistence()"})," can now be used to check if all writes have already been replicated into the parent collection. Also ",(0,a.jsx)(n.code,{children:"RxCollection.watchForChanges()"})," got split out from the ",(0,a.jsx)(n.code,{children:"replication"}),"-plugin into its own ",(0,a.jsx)(n.code,{children:"watch-for-changes"}),"-plugin because it is used in the inMemory and the replication functionality."]}),"\n",(0,a.jsx)(n.h2,{id:"some-comparisons",children:"Some comparisons"}),"\n",(0,a.jsx)(n.p,{children:"(Tested with node 10.6.0 and the memory-adapter, lower is better)"}),"\n",(0,a.jsxs)(n.p,{children:["Reproduce with ",(0,a.jsx)(n.code,{children:"npm run build:size"})," and ",(0,a.jsx)(n.code,{children:"npm run test:performance"})]}),"\n",(0,a.jsxs)(n.table,{children:[(0,a.jsx)(n.thead,{children:(0,a.jsxs)(n.tr,{children:[(0,a.jsx)(n.th,{style:{textAlign:"center"}}),(0,a.jsx)(n.th,{style:{textAlign:"center"},children:"7.7.1"}),(0,a.jsx)(n.th,{children:"8.0.0"})]})}),(0,a.jsxs)(n.tbody,{children:[(0,a.jsxs)(n.tr,{children:[(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"Bundle-Size (Webpack+minify+gzip)"}),(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"110 kb"}),(0,a.jsx)(n.td,{children:"101.962 kb"})]}),(0,a.jsxs)(n.tr,{children:[(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"Spawn 1000 databases with 5 collections each"}),(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"8744 ms"}),(0,a.jsx)(n.td,{children:"9906 ms"})]}),(0,a.jsxs)(n.tr,{children:[(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"insert 2000 documents"}),(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"8006 ms"}),(0,a.jsx)(n.td,{children:"5781 ms"})]}),(0,a.jsxs)(n.tr,{children:[(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"find 10.000 documents"}),(0,a.jsx)(n.td,{style:{textAlign:"center"},children:"3202 ms"}),(0,a.jsx)(n.td,{children:"1312 ms"})]})]})]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>r,x:()=>i});var t=s(6540);const a={},o=t.createContext(a);function r(e){const n=t.useContext(o);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),t.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4616b86a.247bc33b.js b/docs/assets/js/4616b86a.247bc33b.js
deleted file mode 100644
index ec820b92093..00000000000
--- a/docs/assets/js/4616b86a.247bc33b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[465],{3247(e,n,o){o.d(n,{g:()=>r});var s=o(4848);function r(e){const n=[];let o=null;return e.children.forEach(e=>{e.props.id?(o&&n.push(o),o={headline:e,paragraphs:[]}):o&&o.paragraphs.push(e)}),o&&n.push(o),(0,s.jsx)("div",{style:t.stepsContainer,children:n.map((e,n)=>(0,s.jsxs)("div",{style:t.stepWrapper,children:[(0,s.jsxs)("div",{style:t.stepIndicator,children:[(0,s.jsx)("div",{style:t.stepNumber,children:n+1}),(0,s.jsx)("div",{style:t.verticalLine})]}),(0,s.jsxs)("div",{style:t.stepContent,children:[(0,s.jsx)("div",{children:e.headline}),e.paragraphs.map((e,n)=>(0,s.jsx)("div",{style:t.item,children:e},n))]})]},n))})}const t={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},3844(e,n,o){o.r(n),o.d(n,{assets:()=>d,contentTitle:()=>l,default:()=>g,frontMatter:()=>i,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"rx-storage-mongodb","title":"Unlock MongoDB Power with RxDB","description":"Combine RxDB\'s real-time sync with MongoDB\'s scalability. Harness the MongoDB RxStorage to seamlessly expand your database capabilities.","source":"@site/docs/rx-storage-mongodb.md","sourceDirName":".","slug":"/rx-storage-mongodb.html","permalink":"/rx-storage-mongodb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Unlock MongoDB Power with RxDB","slug":"rx-storage-mongodb.html","description":"Combine RxDB\'s real-time sync with MongoDB\'s scalability. Harness the MongoDB RxStorage to seamlessly expand your database capabilities."},"sidebar":"tutorialSidebar","previous":{"title":"Dexie.js","permalink":"/rx-storage-dexie.html"},"next":{"title":"DenoKV","permalink":"/rx-storage-denokv.html"}}');var r=o(4848),t=o(8453),a=o(3247);const i={title:"Unlock MongoDB Power with RxDB",slug:"rx-storage-mongodb.html",description:"Combine RxDB's real-time sync with MongoDB's scalability. Harness the MongoDB RxStorage to seamlessly expand your database capabilities."},l="MongoDB RxStorage",d={},c=[{value:"Limitations of the MongoDB RxStorage",id:"limitations-of-the-mongodb-rxstorage",level:2},{value:"Using the MongoDB RxStorage",id:"using-the-mongodb-rxstorage",level:2},{value:"Install the mongodb package",id:"install-the-mongodb-package",level:3},{value:"Setups the MongoDB RxStorage",id:"setups-the-mongodb-rxstorage",level:3}];function h(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"mongodb-rxstorage",children:"MongoDB RxStorage"})}),"\n",(0,r.jsxs)(n.p,{children:["RxDB MongoDB RxStorage is an RxDB ",(0,r.jsx)(n.a,{href:"/rx-storage.html",children:"RxStorage"})," that allows you to use ",(0,r.jsx)(n.a,{href:"https://www.mongodb.com/",children:"MongoDB"})," as the underlying storage engine for your RxDB database. With this you can take advantage of MongoDB's features and scalability while benefiting from RxDB's real-time data synchronization capabilities."]}),"\n",(0,r.jsx)("p",{align:"center",children:(0,r.jsx)("img",{src:"./files/icons/mongodb.svg",alt:"MongoDB storage",height:"100",class:"img-padding"})}),"\n",(0,r.jsxs)(n.p,{children:["The storage is made to work with any plain MongoDB Server, ",(0,r.jsx)(n.a,{href:"https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set/",children:"MongoDB Replica Set"}),", ",(0,r.jsx)(n.a,{href:"https://www.mongodb.com/docs/manual/sharding/",children:"Sharded MongoDB Cluster"})," or ",(0,r.jsx)(n.a,{href:"https://www.mongodb.com/atlas/database",children:"Atlas Cloud Database"}),"."]}),"\n",(0,r.jsx)(n.h2,{id:"limitations-of-the-mongodb-rxstorage",children:"Limitations of the MongoDB RxStorage"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Multiple Node.js servers using the same MongoDB database is currently not supported"}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"/rx-attachment.html",children:"RxAttachments"})," are currently not supported"]}),"\n",(0,r.jsx)(n.li,{children:"Doing non-RxDB writes on the MongoDB database is not supported. RxDB expects all writes to come from RxDB which update the required metadata. Doing non-RxDB writes can confuse the RxDatabase and lead to undefined behavior. But you can perform read-queries on the MongoDB storage from the outside at any time."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"using-the-mongodb-rxstorage",children:"Using the MongoDB RxStorage"}),"\n",(0,r.jsxs)(a.g,{children:[(0,r.jsx)(n.h3,{id:"install-the-mongodb-package",children:"Install the mongodb package"}),(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" mongodb"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" --save"})]})})})}),(0,r.jsx)(n.h3,{id:"setups-the-mongodb-rxstorage",children:"Setups the MongoDB RxStorage"}),(0,r.jsxs)(n.p,{children:["To use the storage, you simply import the ",(0,r.jsx)(n.code,{children:"getRxStorageMongoDB"})," method and use that when creating the ",(0,r.jsx)(n.a,{href:"/rx-database.html",children:"RxDatabase"}),". The ",(0,r.jsx)(n.code,{children:"connection"})," parameter contains the ",(0,r.jsx)(n.a,{href:"https://www.mongodb.com/docs/manual/reference/connection-string/",children:"MongoDB connection string"}),"."]}),(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageMongoDB"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-mongodb'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMongoDB"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * MongoDB connection string"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" https://www.mongodb.com/docs/manual/reference/connection-string/"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" connection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mongodb://localhost:27017,localhost:27018,localhost:27019'"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]})]})}function g(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},8453(e,n,o){o.d(n,{R:()=>a,x:()=>i});var s=o(6540);const r={},t=s.createContext(r);function a(e){const n=s.useContext(t);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),s.createElement(t.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4777fd9a.5d703faa.js b/docs/assets/js/4777fd9a.5d703faa.js
deleted file mode 100644
index 4bb061e719a..00000000000
--- a/docs/assets/js/4777fd9a.5d703faa.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6386],{692(e,a,i){i.r(a),i.d(a,{assets:()=>l,contentTitle:()=>r,default:()=>h,frontMatter:()=>o,metadata:()=>t,toc:()=>c});const t=JSON.parse('{"id":"alternatives","title":"Alternatives for realtime local-first JavaScript applications and local databases","description":"Explore real-time, local-first JS alternatives to RxDB. Compare Firebase, Meteor, AWS, CouchDB, and more for robust, seamless web/mobile app development.","source":"@site/docs/alternatives.md","sourceDirName":".","slug":"/alternatives.html","permalink":"/alternatives.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Alternatives for realtime local-first JavaScript applications and local databases","slug":"alternatives.html","description":"Explore real-time, local-first JS alternatives to RxDB. Compare Firebase, Meteor, AWS, CouchDB, and more for robust, seamless web/mobile app development."},"sidebar":"tutorialSidebar","previous":{"title":"Capacitor Database Guide - SQLite, RxDB & More","permalink":"/capacitor-database.html"},"next":{"title":"Electron Database - Storage adapters for SQLite, Filesystem and In-Memory","permalink":"/electron-database.html"}}');var s=i(4848),n=i(8453);const o={title:"Alternatives for realtime local-first JavaScript applications and local databases",slug:"alternatives.html",description:"Explore real-time, local-first JS alternatives to RxDB. Compare Firebase, Meteor, AWS, CouchDB, and more for robust, seamless web/mobile app development."},r="Alternatives for realtime offline-first JavaScript applications",l={},c=[{value:"Alternatives to RxDB",id:"alternatives-to-rxdb",level:2},{value:"Firebase",id:"firebase",level:3},{value:"Firebase - Realtime Database",id:"firebase---realtime-database",level:4},{value:"Firebase - Cloud Firestore",id:"firebase---cloud-firestore",level:4},{value:"Meteor",id:"meteor",level:3},{value:"Minimongo",id:"minimongo",level:3},{value:"WatermelonDB",id:"watermelondb",level:3},{value:"AWS Amplify",id:"aws-amplify",level:3},{value:"AWS Datastore",id:"aws-datastore",level:3},{value:"RethinkDB",id:"rethinkdb",level:3},{value:"Horizon",id:"horizon",level:3},{value:"Supabase",id:"supabase",level:3},{value:"CouchDB",id:"couchdb",level:3},{value:"PouchDB",id:"pouchdb",level:3},{value:"Couchbase",id:"couchbase",level:3},{value:"Cloudant",id:"cloudant",level:3},{value:"Hoodie",id:"hoodie",level:3},{value:"LokiJS",id:"lokijs",level:3},{value:"Gundb",id:"gundb",level:3},{value:"sql.js",id:"sqljs",level:3},{value:"absurd-sQL",id:"absurd-sql",level:3},{value:"NeDB",id:"nedb",level:3},{value:"Dexie.js",id:"dexiejs",level:3},{value:"LowDB",id:"lowdb",level:3},{value:"localForage",id:"localforage",level:3},{value:"MongoDB Realm",id:"mongodb-realm",level:3},{value:"Apollo",id:"apollo",level:3},{value:"Replicache",id:"replicache",level:3},{value:"InstantDB",id:"instantdb",level:3},{value:"Yjs",id:"yjs",level:3},{value:"ElectricSQL",id:"electricsql",level:3},{value:"SignalDB",id:"signaldb",level:3},{value:"PowerSync",id:"powersync",level:3}];function d(e){const a={a:"a",admonition:"admonition",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,n.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.header,{children:(0,s.jsx)(a.h1,{id:"alternatives-for-realtime-offline-first-javascript-applications",children:"Alternatives for realtime offline-first JavaScript applications"})}),"\n",(0,s.jsxs)(a.p,{children:["To give you an augmented view over the topic of client side JavaScript databases, this page contains all known alternatives to ",(0,s.jsx)(a.strong,{children:"RxDB"}),". Remember that you are reading this inside of the RxDB documentation, so everything is ",(0,s.jsx)(a.strong,{children:"opinionated"}),".\nIf you disagree with anything or think that something is missing, make a pull request to this file on the RxDB github repository."]}),"\n",(0,s.jsxs)(a.admonition,{type:"note",children:[(0,s.jsx)(a.p,{children:"RxDB has these main benefits:"}),(0,s.jsxs)(a.ul,{children:["\n",(0,s.jsxs)(a.li,{children:["RxDB is a battle proven tool ",(0,s.jsx)(a.a,{href:"/#reviews",children:"widely used"})," by companies in real projects in production."]}),"\n",(0,s.jsxs)(a.li,{children:["RxDB is not VC funded and therefore does not require you to use a specific cloud service to rip you off. RxDB can be used with your ",(0,s.jsx)(a.a,{href:"/replication-http.html",children:"own backend"})," or no backend at all."]}),"\n",(0,s.jsxs)(a.li,{children:["RxDB has a working business model of selling ",(0,s.jsx)(a.a,{href:"/premium/",children:"premium plugins"})," which ensures that RxDB will be maintained and improved continuously while many alternatives are dead already or seem to die soon."]}),"\n",(0,s.jsxs)(a.li,{children:["RxDB has years (since 2016) of performance optimization, bug fixing and feature adding. It is just working as is and there are close to zero ",(0,s.jsx)(a.a,{href:"https://github.com/pubkey/rxdb/issues",children:"open issues"}),"."]}),"\n"]}),(0,s.jsx)("center",{children:(0,s.jsx)("a",{href:"https://rxdb.info/",children:(0,s.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})})]}),"\n",(0,s.jsx)(a.hr,{}),"\n",(0,s.jsx)(a.h2,{id:"alternatives-to-rxdb",children:"Alternatives to RxDB"}),"\n",(0,s.jsxs)(a.p,{children:[(0,s.jsx)(a.a,{href:"https://rxdb.info",children:"RxDB"})," is an ",(0,s.jsx)(a.strong,{children:"observable"}),", ",(0,s.jsx)(a.strong,{children:"replicating"}),", ",(0,s.jsx)(a.strong,{children:(0,s.jsx)(a.a,{href:"/offline-first.html",children:"local first"})}),", ",(0,s.jsx)(a.strong,{children:"JavaScript"})," database. So it makes only sense to list similar projects as alternatives, not just any database or JavaScript store library. However, I will list up some projects that RxDB is often compared with, even if it only makes sense for some use cases.\nHere are the alternatives to RxDB:"]}),"\n",(0,s.jsx)(a.h3,{id:"firebase",children:"Firebase"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/alternatives/firebase.svg",alt:"firebase alternative",class:"img-padding",height:"60"})}),"\n",(0,s.jsxs)(a.p,{children:["Firebase is a ",(0,s.jsx)(a.strong,{children:"platform"})," developed by Google for creating mobile and web applications. Firebase has many features and products, two of which are client side databases. The ",(0,s.jsx)(a.a,{href:"/articles/firebase-realtime-database-alternative.html",children:"Realtime Database"})," and the ",(0,s.jsx)(a.a,{href:"/articles/firestore-alternative.html",children:"Cloud Firestore"}),"."]}),"\n",(0,s.jsx)(a.h4,{id:"firebase---realtime-database",children:"Firebase - Realtime Database"}),"\n",(0,s.jsxs)(a.p,{children:['The firebase realtime database was the first database in firestore. It has to be mentioned that in this context, "realtime" means ',(0,s.jsx)(a.strong,{children:'"realtime replication"'}),', not "realtime computing". The firebase realtime database stores data as a big unstructured JSON tree that is replicated between clients and the backend.']}),"\n",(0,s.jsx)(a.h4,{id:"firebase---cloud-firestore",children:"Firebase - Cloud Firestore"}),"\n",(0,s.jsxs)(a.p,{children:["The firestore is the successor to the realtime database. The big difference is that it behaves more like a 'normal' database that stores data as documents inside of collections. The conflict resolution strategy of firestore is always ",(0,s.jsx)(a.em,{children:"last-write-wins"})," which might or might not be suitable for your use case."]}),"\n",(0,s.jsxs)(a.p,{children:["The biggest difference to RxDB is that firebase products are only able to be used on top of the Firebase cloud hosted backend, which creates a vendor lock-in. RxDB can replicate with any self hosted CouchDB server or custom GraphQL endpoints. You can even replicate Firestore to RxDB with the ",(0,s.jsx)(a.a,{href:"/replication-firestore.html",children:"Firestore Replication Plugin"}),"."]}),"\n",(0,s.jsx)(a.h3,{id:"meteor",children:"Meteor"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/alternatives/meteor_text.svg",alt:"MeteorJS alternative",class:"img-padding",height:"60"})}),"\n",(0,s.jsxs)(a.p,{children:["Meteor (since 2012) is one of the oldest technologies for JavaScript realtime applications. Meteor is not a library but a whole framework with its own package manager, database management and replication.\nBecause of how it works, it has proven to be hard to integrate it with other modern JavaScript frameworks like ",(0,s.jsx)(a.a,{href:"https://github.com/urigo/angular-meteor",children:"angular"}),", ",(0,s.jsx)(a.a,{href:"/articles/vue-database.html",children:"vue.js"})," or svelte."]}),"\n",(0,s.jsxs)(a.p,{children:["Meteor uses MongoDB in the backend and can replicate with a Minimongo database in the frontend.\nWhile testing, it has proven to be impossible to make a meteor app ",(0,s.jsx)(a.strong,{children:"offline first"})," capable. There are ",(0,s.jsx)(a.a,{href:"https://github.com/frozeman/meteor-persistent-minimongo2",children:"some projects"})," that might do this, but all are unmaintained."]}),"\n",(0,s.jsx)(a.h3,{id:"minimongo",children:"Minimongo"}),"\n",(0,s.jsxs)(a.p,{children:["Forked in Jan 2014 from meteorJSs' minimongo package, Minimongo is a client-side, in-memory, JavaScript version of MongoDB with backend replication over HTTP. Similar to MongoDB, it stores data in documents inside of collections and also has the same query syntax. Minimongo has different storage adapters for IndexedDB, WebSQL, ",(0,s.jsx)(a.a,{href:"/articles/localstorage.html",children:"LocalStorage"})," and SQLite.\nCompared to RxDB, Minimongo has no concept of revisions or conflict handling, which might lead to undefined behavior when used with replication or in multiple browser tabs. Minimongo has no observable queries or changestream."]}),"\n",(0,s.jsx)(a.h3,{id:"watermelondb",children:"WatermelonDB"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/alternatives/watermelondb.png",alt:"WatermelonDB alternative",height:"60"})}),"\n",(0,s.jsxs)(a.p,{children:["WatermelonDB is a reactive & asynchronous JavaScript database. While originally made for ",(0,s.jsx)(a.a,{href:"/articles/react-database.html",children:"React"})," and ",(0,s.jsx)(a.a,{href:"/react-native-database.html",children:"React Native"}),", it can also be used with other JavaScript frameworks. The main goal of WatermelonDB is ",(0,s.jsx)(a.strong,{children:"performance"})," within an application with lots of data.\nIn React Native, WatermelonDB uses the provided SQLite database. Also there is an Expo plugin for WatermelonDB. In a browser, WatermelonDB uses the LokiJS in-memory database to store and query data. WatermelonDB is one of the rare projects that support both Flow and Typescript at the same time."]}),"\n",(0,s.jsx)(a.h3,{id:"aws-amplify",children:"AWS Amplify"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/alternatives/aws-amplify.svg",alt:"AWS Amplify alternative",height:"60",class:"img-padding"})}),"\n",(0,s.jsxs)(a.p,{children:["AWS Amplify is a collection of tools and libraries to develop web- and mobile frontend applications. Similar to firebase, it provides everything needed like authentication, analytics, a REST API, storage and so on. Everything hosted in the AWS Cloud, even when they state that ",(0,s.jsx)(a.em,{children:'"AWS Amplify is designed to be open and pluggable for any custom backend or service"'}),". For realtime replication, AWS Amplify can connect to an AWS App-Sync GraphQL endpoint."]}),"\n",(0,s.jsx)(a.h3,{id:"aws-datastore",children:"AWS Datastore"}),"\n",(0,s.jsxs)(a.p,{children:["Since December 2019 the Amplify library includes the AWS Datastore which is a document-based, client side database that is able to replicate data via AWS AppSync in the background.\nThe main difference to other projects is the complex project configuration via the amplify cli and the bit confusing query syntax that works over functions. Complex Queries with multiple ",(0,s.jsx)(a.code,{children:"OR/AND"})," statements are not possible which might change in the future.\nLocal development is hard because the AWS AppSync mock does not support realtime replication. It also is not really offline-first because a user login is always required."]}),"\n",(0,s.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,s.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,s.jsxs)(a.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,s.jsx)(a.span,{"data-line":"",children:(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// An AWS datastore OR query"})}),"\n",(0,s.jsxs)(a.span,{"data-line":"",children:[(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" posts"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" DataStore"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".query"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(Post"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" c "}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" c"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".or"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,s.jsxs)(a.span,{"data-line":"",children:[(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" c "}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" c"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".rating"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"gt"'}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" 4"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".status"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"eq"'}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" PostStatus"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"PUBLISHED"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,s.jsx)(a.span,{"data-line":"",children:(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"));"})}),"\n",(0,s.jsx)(a.span,{"data-line":"",children:" "}),"\n",(0,s.jsx)(a.span,{"data-line":"",children:(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// An AWS datastore SORT query"})}),"\n",(0,s.jsxs)(a.span,{"data-line":"",children:[(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" posts"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" DataStore"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".query"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(Post"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" Predicates"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"ALL"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,s.jsxs)(a.span,{"data-line":"",children:[(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" sort"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" s "}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" s"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".rating"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"SortDirection"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"ASCENDING"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".title"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"SortDirection"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"DESCENDING"}),(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,s.jsx)(a.span,{"data-line":"",children:(0,s.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,s.jsx)(a.p,{children:"The biggest difference to RxDB is that you have to use the AWS cloud backends. This might not be a problem if your data is at AWS anyway."}),"\n",(0,s.jsx)(a.h3,{id:"rethinkdb",children:"RethinkDB"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/alternatives/rethinkdb.svg",alt:"RethinkDB alternative",height:"60",class:"img-padding"})}),"\n",(0,s.jsx)(a.p,{children:"RethinkDB is a backend database that pushed dynamic JSON data to the client in realtime. It was founded in 2009 and the company shut down in 2016.\nRethink db is not a client side database, it streams data from the backend to the client which of course does not work while offline."}),"\n",(0,s.jsx)(a.h3,{id:"horizon",children:"Horizon"}),"\n",(0,s.jsxs)(a.p,{children:["Horizon is the client side library for RethinkDB which provides useful functions like authentication, permission management and subscription to a RethinkDB backend. Offline support ",(0,s.jsx)(a.a,{href:"https://github.com/rethinkdb/horizon/issues/58",children:"never made"})," it to horizon."]}),"\n",(0,s.jsx)(a.h3,{id:"supabase",children:"Supabase"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/icons/supabase.svg",alt:"Supabase alternative",height:"60",class:"img-padding"})}),"\n",(0,s.jsxs)(a.p,{children:['Supabase labels itself as "',(0,s.jsx)(a.em,{children:"an open source Firebase alternative"}),'". It is a collection of open source tools that together mimic many Firebase features, most of them by providing a wrapper around a PostgreSQL database. While it has realtime queries that run over the wire, like with RethinkDB, Supabase has no client-side storage or replication feature and therefore is not offline first.']}),"\n",(0,s.jsx)(a.h3,{id:"couchdb",children:"CouchDB"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/icons/couchdb-text.svg",alt:"CouchDB alternative",height:"60",class:"img-padding"})}),"\n",(0,s.jsx)(a.p,{children:"Apache CouchDB is a server-side, document-oriented database that is mostly known for its multi-master replication feature. Instead of having a master-slave replication, with CouchDB you can run replication in any constellation without having a master server as bottleneck where the server even can go off- and online at any time. This comes with the drawback of having a slow replication with much network overhead.\nCouchDB has a changestream and a query syntax similar to MongoDB."}),"\n",(0,s.jsx)(a.h3,{id:"pouchdb",children:"PouchDB"}),"\n",(0,s.jsx)("p",{align:"center",children:(0,s.jsx)("img",{src:"./files/alternatives/pouchdb.svg",alt:"PouchDB alternative",height:"60",class:"img-padding"})}),"\n",(0,s.jsxs)(a.p,{children:["PouchDB is a JavaScript database that is compatible with most of the CouchDB API. It has an adapter system that allows you to switch out the underlying storage layer. There are many adapters like for ",(0,s.jsx)(a.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"}),", ",(0,s.jsx)(a.a,{href:"/rx-storage-sqlite.html",children:"SQLite"}),", the Filesystem and so on. The main benefit is to be able to replicate data with any CouchDB compatible endpoint.\nBecause of the CouchDB compatibility, PouchDB has to do a lot of overhead in handling the revision tree of document, which is why it can show bad performance for bigger datasets.\nRxDB was originally build around PouchDB until the storage layer was abstracted out in version ",(0,s.jsx)(a.a,{href:"/releases/10.0.0.html",children:"10.0.0"})," so it now allows to use different ",(0,s.jsx)(a.code,{children:"RxStorage"})," implementations. PouchDB has some performance issues because of how it has to store the document revision tree to stay compatible with the CouchDB API."]}),"\n",(0,s.jsx)(a.h3,{id:"couchbase",children:"Couchbase"}),"\n",(0,s.jsxs)(a.p,{children:["Couchbase (originally known as Membase) is another NoSQL document database made for realtime applications.\nIt uses the N1QL query language which is more SQL like compared to other NoSQL query languages. In theory you can achieve replication of a Couchbase with a PouchDB database, but this has shown to be not ",(0,s.jsx)(a.a,{href:"https://github.com/pouchdb/pouchdb/issues/7793#issuecomment-501624297",children:"that easy"}),"."]}),"\n",(0,s.jsx)(a.h3,{id:"cloudant",children:"Cloudant"}),"\n",(0,s.jsxs)(a.p,{children:["Cloudant is a cloud-based service that is based on ",(0,s.jsx)(a.a,{href:"/replication-couchdb.html",children:"CouchDB"})," and has mostly the same features.\nIt was originally designed for cloud computing where data can automatically be distributed between servers. But it can also be used to replicate with frontend PouchDB instances to create scalable web applications.\nIt was bought by IBM in 2014 and since 2018 the Cloudant Shared Plan is retired and migrated to IBM Cloud."]}),"\n",(0,s.jsx)(a.h3,{id:"hoodie",children:"Hoodie"}),"\n",(0,s.jsx)(a.p,{children:"Hoodie is a backend solution that enables offline-first JavaScript frontend development without having to write backend code. Its main goal is to abstract away configuration into simple calls to the Hoodie API.\nIt uses CouchDB in the backend and PouchDB in the frontend to enable offline-first capabilities.\nThe last commit for hoodie was one year ago and the website (hood.ie) is offline which indicates it is not an active project anymore."}),"\n",(0,s.jsx)(a.h3,{id:"lokijs",children:"LokiJS"}),"\n",(0,s.jsxs)(a.p,{children:["LokiJS is a JavaScript embeddable, in-memory database. And because everything is handled in-memory, LokiJS has awesome performance when mutating or querying data. You can still persist to a permanent storage (IndexedDB, Filesystem etc.) with one of the provided storage adapters. The persistence happens after a timeout is reached after a write, or before the JavaScript process exits. This also means you could loose data when the JavaScript process exits ungracefully like when the power of the device is shut down or the browser crashes.\nWhile the project is not that active anymore, it is more ",(0,s.jsx)(a.em,{children:"finished"})," than ",(0,s.jsx)(a.em,{children:"unmaintained"}),"."]}),"\n",(0,s.jsxs)(a.p,{children:["In the past, RxDB supported using ",(0,s.jsx)(a.a,{href:"/rx-storage-lokijs.html",children:"LokiJS as RxStorage"})," but because the LokiJS is not maintained anymore and had too many issues, this storage option was removed in RxDB version 16."]}),"\n",(0,s.jsx)(a.h3,{id:"gundb",children:"Gundb"}),"\n",(0,s.jsxs)(a.p,{children:["GUN is a JavaScript graph database. While having many features, the ",(0,s.jsx)(a.strong,{children:"decentralized"})," replication is the main unique selling point. You can replicate data Peer-to-Peer without any centralized backend server. GUN has several other features that are useful on top of that, like encryption and authentication."]}),"\n",(0,s.jsxs)(a.p,{children:["While testing it was really hard to get basic things running. GUN is open source, but because of how the source code ",(0,s.jsx)(a.a,{href:"https://github.com/amark/gun/blob/master/src/put.js",children:"is written"}),", it is very difficult to understand what is going wrong."]}),"\n",(0,s.jsx)(a.h3,{id:"sqljs",children:"sql.js"}),"\n",(0,s.jsx)(a.p,{children:"sql.js is a javascript library to run SQLite on the web. It uses a virtual database file stored in memory and does not have any persistence. All data is lost once the JavaScript process exits. sql.js is created by compiling SQLite to WebAssembly so it has about the same features as SQLite. For older browsers there is a JavaScript fallback."}),"\n",(0,s.jsx)(a.h3,{id:"absurd-sql",children:"absurd-sQL"}),"\n",(0,s.jsxs)(a.p,{children:["Absurd-sql is a project that implements an IndexedDB-based persistence for sql.js. Instead of directly writing data into the IndexedDB, it treats IndexedDB like a disk and stores data in blocks there which shows to have a much better performance, mostly because of how ",(0,s.jsx)(a.a,{href:"/slow-indexeddb.html",children:"performance expensive"})," IndexedDB transactions are."]}),"\n",(0,s.jsx)(a.h3,{id:"nedb",children:"NeDB"}),"\n",(0,s.jsxs)(a.p,{children:["NeDB was a embedded persistent or in-memory database for Node.js, nw.js, ",(0,s.jsx)(a.a,{href:"/electron-database.html",children:"Electron"})," and browsers.\nIt is document-oriented and had the same query syntax as MongoDB.\nLike LokiJS it has persistence adapters for IndexedDB etc. to persist the database state on the disc.\nThe last commit to NeDB was in ",(0,s.jsx)(a.strong,{children:"2016"}),"."]}),"\n",(0,s.jsx)(a.h3,{id:"dexiejs",children:"Dexie.js"}),"\n",(0,s.jsx)(a.p,{children:"Dexie.js is a minimalistic wrapper for IndexedDB. While providing a better API than plain IndexedDB, Dexie also improves performance by batching transactions and other optimizations. It also adds additional non-IndexedDB features like observable queries or multi tab support or react hooks.\nCompared to RxDB, Dexie.js does not support complex (MongoDB-like) queries and requires a lot of fiddling when a document range of a specific index must be fetched.\nDexie.js is used by Whatsapp Web, Microsoft To Do and Github Desktop."}),"\n",(0,s.jsxs)(a.p,{children:["RxDB supports using ",(0,s.jsx)(a.a,{href:"/rx-storage-dexie.html",children:"Dexie.js as Database storage"})," which enhances IndexedDB via dexie with RxDB features like MongoDB-like queries etc."]}),"\n",(0,s.jsx)(a.h3,{id:"lowdb",children:"LowDB"}),"\n",(0,s.jsx)(a.p,{children:"LowDB is a small, local JSON database powered by the Lodash library. It is designed to be simple, easy to use, and straightforward. LowDB allows you to perform native JavaScript queries and persist data in a flat JSON file. Written in TypeScript, it's particularly well-suited for small projects, prototyping, or when you need a lightweight, file-based database."}),"\n",(0,s.jsxs)(a.p,{children:["As an alternative to LowDB, ",(0,s.jsx)(a.a,{href:"./",children:"RxDB"})," offers real-time reactivity, allowing developers to subscribe to database changes, a feature not natively available in LowDB. Additionally, RxDB provides robust ",(0,s.jsx)(a.a,{href:"/rx-query.html",children:"query capabilities"}),", including the ability to subscribe to query results for automatic UI updates. These features make RxDB a strong alternative to LowDB for more complex and dynamic applications."]}),"\n",(0,s.jsx)(a.h3,{id:"localforage",children:"localForage"}),"\n",(0,s.jsxs)(a.p,{children:["localForage is a popular JavaScript library for offline storage that provides a simple, promise-based API. It abstracts over different storage mechanisms such as ",(0,s.jsx)(a.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"}),", WebSQL, or ",(0,s.jsx)(a.a,{href:"/articles/localstorage.html",children:"localStorage"}),", making it easier to write code once and have it work seamlessly across various browsers. While localForage is great for storing data locally in a key-value manner, it doesn't provide the real-time reactive queries, ",(0,s.jsx)(a.a,{href:"/transactions-conflicts-revisions.html",children:"conflict handling"}),", or revision-based replication that RxDB does. This makes localForage a useful choice for straightforward caching or persistent storage needs, but not ideal for advanced offline-first scenarios requiring multi-user collaboration or complex querying."]}),"\n",(0,s.jsx)(a.h3,{id:"mongodb-realm",children:"MongoDB Realm"}),"\n",(0,s.jsx)(a.p,{children:"Originally Realm was a mobile database for Android and iOS. Later they added support for other languages and runtimes, also for JavaScript.\nIt was meant as replacement for SQLite but is more like an object store than a full SQL database.\nIn 2019 MongoDB bought Realm and changed the projects focus.\nNow Realm is made for replication with the MongoDB Realm Sync based on the MongoDB Atlas Cloud platform. This tight coupling to the MongoDB cloud service is a big downside for most use cases."}),"\n",(0,s.jsx)(a.h3,{id:"apollo",children:"Apollo"}),"\n",(0,s.jsx)(a.p,{children:"The Apollo GraphQL platform is made to transfer data between a server to UI applications over GraphQL endpoints. It contains several tools like GraphQL clients in different languages or libraries to create GraphQL endpoints."}),"\n",(0,s.jsx)(a.p,{children:"While it is has different caching features for offline usage, compared to RxDB it is not fully offline first because caching alone does not mean your application is fully usable when the user is offline."}),"\n",(0,s.jsx)(a.h3,{id:"replicache",children:"Replicache"}),"\n",(0,s.jsxs)(a.p,{children:["Replicache is a client-side sync framework for building realtime, collaborative, ",(0,s.jsx)(a.a,{href:"/articles/local-first-future.html",children:"local-first"})," web apps. It claims to work with most backend stacks. In contrast to other local first tools, replicache does not work like a local database. Instead it runs on so called ",(0,s.jsx)(a.code,{children:"mutators"})," that unify behavior on the client and server side. So instead of implementing and calling REST routes on both sides of your stack, you will implement mutators that define a specific delta behavior based on the input data. To observe data in replicache, there are ",(0,s.jsx)(a.code,{children:"subscriptions"})," that notify your frontend application about changes to the state.\nReplicache can be used in most frontend technologies like browsers, React/Remix, NextJS/Vercel and React Native. While Replicache can be installed and used from npm, the Replicache source code is not open source and the Replicache github repo does not allow you to inspect or debug it. Still you can use replicache for in non-commercial projects, or for companies with < $200k revenue (ARR) and < $500k in funding. (2024: Replicache will be free and Rocicorp are working on a new Zerosync product to succeed Replicache and Reflect.)"]}),"\n",(0,s.jsx)(a.h3,{id:"instantdb",children:"InstantDB"}),"\n",(0,s.jsxs)(a.p,{children:["InstantDB is designed for real-time data synchronization with built-in offline support, allowing changes to be queued locally and ",(0,s.jsx)(a.a,{href:"/replication.html",children:"synced"})," when the user reconnects. While it offers seamless ",(0,s.jsx)(a.a,{href:"/articles/optimistic-ui.html",children:"optimistic updates"})," and rollback capabilities, its offline-first design is not as mature or comprehensive as RxDB's - the ",(0,s.jsx)(a.a,{href:"/articles/offline-database.html",children:"offline data"})," is more of a cache, not a full-database sync. The query language used is Datalog, and the backend sync service is written in Clojure. InstantDB is focused more on simplicity and real-time collaboration, with fewer customization options for storage or conflict resolution compared to RxDB, which supports various storage adapters and advanced conflict handling via CRDTs."]}),"\n",(0,s.jsx)(a.h3,{id:"yjs",children:"Yjs"}),"\n",(0,s.jsxs)(a.p,{children:["Yjs is a ",(0,s.jsx)(a.a,{href:"/crdt.html",children:"CRDT-based"})," (Conflict-free Replicated Data Type) library focused on enabling real-time collaboration - particularly for text editing, although it can handle other data types as well. While it provides powerful conflict resolution and peer-to-peer synchronization out of the box, Yjs itself is not a full-fledged database. Instead, you typically combine Yjs with other storage or networking layers to achieve a ",(0,s.jsx)(a.a,{href:"/offline-first.html",children:"local-first architecture"}),". This flexibility allows for sophisticated ",(0,s.jsx)(a.a,{href:"/articles/realtime-database.html",children:"real-time"})," features, but also means you must handle indexing, queries, and persistence on your own if you need them. Compared to RxDB, Yjs does not offer built-in replication adapters or a query system, so developers who require a more complete solution for conflict resolution, data persistence, and offline-first capabilities may find RxDB more convenient."]}),"\n",(0,s.jsx)(a.h3,{id:"electricsql",children:"ElectricSQL"}),"\n",(0,s.jsx)(a.p,{children:'2024: ElectricSQL is being rewritten in a new Electric-Next branch, which focuses on partial syncing of ("shapes", which makes is basically a NoSQL like document database) of data from a remote Postgres DB to a local clients written in TypeScript/JS or Elixir. The write path is not yet implemented, neither is client-side reactivity. The ElectricSQL backend is written in Elixir.'}),"\n",(0,s.jsx)(a.h3,{id:"signaldb",children:"SignalDB"}),"\n",(0,s.jsx)(a.p,{children:"SignalDB provides a reactive, in-memory local-lirst JavaScript database with real-time sync, bit it doesn't offer the same level of multi-client replication or flexibility with storage backends that RxDB provides, and through a RxDB persistence adapters you can actually use SignalDB for the front-end reactivity while relying on RxDB for backend sync and persistence."}),"\n",(0,s.jsx)(a.h3,{id:"powersync",children:"PowerSync"}),"\n",(0,s.jsx)(a.p,{children:'PowerSync is a "framework" for implementing local-first solutions. It centralizes business logic and conflict resolution on a central, authoritative server (PostgreSQL or MongoDB), vs RxDB that also supports custom backends. Both RxDB and PowerSync can be used with a variety of storage backends, but PowerSync uses SQLite as the front-end database which has shown to be slow because the WASM-SQLite abstraction increases read and write latency. In terms of client SDKs, PowerSync offers Flutter, Kotlin, and Swift in addition to JS/TypeScript. PowerSync offers man client technologies, PowerSync is under a license that restricts commercial use that competes with PowerSync and the JourneyApps Platform.'}),"\n",(0,s.jsx)(a.h1,{id:"read-further",children:"Read further"}),"\n",(0,s.jsxs)(a.ul,{children:["\n",(0,s.jsx)(a.li,{children:(0,s.jsx)(a.a,{href:"https://github.com/pubkey/client-side-databases",children:"Offline First Database Comparison"})}),"\n"]})]})}function h(e={}){const{wrapper:a}={...(0,n.R)(),...e.components};return a?(0,s.jsx)(a,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},8453(e,a,i){i.d(a,{R:()=>o,x:()=>r});var t=i(6540);const s={},n=t.createContext(s);function o(e){const a=t.useContext(n);return t.useMemo(function(){return"function"==typeof e?e(a):{...a,...e}},[a,e])}function r(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),t.createElement(n.Provider,{value:a},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4adf80bb.5245b2b7.js b/docs/assets/js/4adf80bb.5245b2b7.js
deleted file mode 100644
index 57583ceb431..00000000000
--- a/docs/assets/js/4adf80bb.5245b2b7.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9548],{7014(e,s,r){r.r(s),r.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>d,frontMatter:()=>i,metadata:()=>n,toc:()=>h});const n=JSON.parse('{"id":"rx-storage-pouchdb","title":"PouchDB RxStorage - Migrate for Better Performance","description":"Discover why PouchDB RxStorage is deprecated in RxDB. Learn its legacy, performance drawbacks, and how to upgrade to a faster solution.","source":"@site/docs/rx-storage-pouchdb.md","sourceDirName":".","slug":"/rx-storage-pouchdb.html","permalink":"/rx-storage-pouchdb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"PouchDB RxStorage - Migrate for Better Performance","slug":"rx-storage-pouchdb.html","description":"Discover why PouchDB RxStorage is deprecated in RxDB. Learn its legacy, performance drawbacks, and how to upgrade to a faster solution."}}');var o=r(4848),a=r(8453);const i={title:"PouchDB RxStorage - Migrate for Better Performance",slug:"rx-storage-pouchdb.html",description:"Discover why PouchDB RxStorage is deprecated in RxDB. Learn its legacy, performance drawbacks, and how to upgrade to a faster solution."},t="RxStorage PouchDB",l={},h=[{value:"Why is the PouchDB RxStorage deprecated?",id:"why-is-the-pouchdb-rxstorage-deprecated",level:2},{value:"Pros",id:"pros",level:2},{value:"Cons",id:"cons",level:2},{value:"Usage",id:"usage",level:2},{value:"Polyfill the global variable",id:"polyfill-the-global-variable",level:2},{value:"Adapters",id:"adapters",level:2},{value:"Using the internal PouchDB Database",id:"using-the-internal-pouchdb-database",level:2}];function c(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"rxstorage-pouchdb",children:"RxStorage PouchDB"})}),"\n",(0,o.jsxs)(s.p,{children:["The PouchDB RxStorage is based on the ",(0,o.jsx)(s.a,{href:"https://github.com/pouchdb/pouchdb",children:"PouchDB"})," database. It is the most battle proven RxStorage and has a big ecosystem of adapters. PouchDB does a lot of overhead to enable CouchDB replication which makes the PouchDB RxStorage one of the slowest."]}),"\n",(0,o.jsx)(s.admonition,{type:"warning",children:(0,o.jsxs)(s.p,{children:["The PouchDB RxStorage is removed from RxDB and can no longer be used in new projects. You should switch to a different ",(0,o.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"}),"."]})}),"\n",(0,o.jsx)(s.h2,{id:"why-is-the-pouchdb-rxstorage-deprecated",children:"Why is the PouchDB RxStorage deprecated?"}),"\n",(0,o.jsxs)(s.p,{children:["When I started developing RxDB in 2016, I had a specific use case to solve.\nBecause there was no client-side database out there that fitted, I created\nRxDB as a wrapper around PouchDB. This worked great and all the PouchDB features\nlike the query engine, the adapter system, CouchDB-replication and so on, came for free.\nBut over the years, it became clear that PouchDB is not suitable for many applications,\nmostly because of its performance: To be compliant to CouchDB, PouchDB has to store all\nrevision trees of documents which slows down queries. Also purging these document revisions ",(0,o.jsx)(s.a,{href:"https://github.com/pouchdb/pouchdb/issues/802",children:"is not possible"}),"\nso the database storage size will only increase over time.\nAnother problem was that many issues in PouchDB have never been fixed, but only closed by the issue-bot like ",(0,o.jsx)(s.a,{href:"https://github.com/pouchdb/pouchdb/issues/6454",children:"this one"}),". The whole PouchDB RxStorage code was full of ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/285c3cf6008b3cc83bd9b9946118a621434f0cff/src/plugins/pouchdb/pouch-statics.ts#L181",children:"workarounds and monkey patches"})," to resolve\nthese issues for RxDB users. Many these patches decreased performance even further. Sometimes it was not possible to fix things from the outside, for example queries with ",(0,o.jsx)(s.code,{children:"$gt"})," operators return ",(0,o.jsx)(s.a,{href:"https://github.com/pouchdb/pouchdb/pull/8471",children:"the wrong documents"})," which is a no-go for a production database\nand hard to debug."]}),"\n",(0,o.jsxs)(s.p,{children:["In version ",(0,o.jsx)(s.a,{href:"/releases/10.0.0.html",children:"10.0.0"})," RxDB introduced the ",(0,o.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," layer which\nallows users to swap out the underlying storage engine where RxDB stores and queries documents from.\nThis allowed to use alternatives from PouchDB, for example the ",(0,o.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"})," in browsers\nor even the ",(0,o.jsx)(s.a,{href:"/rx-storage-foundationdb.html",children:"FoundationDB RxStorage"})," on the server side.\nThere where not many use cases left where it was a good choice to use the PouchDB RxStorage. Only replicating with a\nCouchDB server, was only possible with PouchDB. But this has also changed. RxDB has ",(0,o.jsx)(s.a,{href:"/replication-couchdb.html",children:"a plugin"})," that allows\nto replicate clients with any CouchDB server by using the ",(0,o.jsx)(s.a,{href:"/replication.html",children:"RxDB Sync Engine"}),". This plugins work with any RxStorage so that it is not necessary to use the PouchDB storage.\nRemoving PouchDB allows RxDB to add many awaited features like filtered change streams for easier replication and permission handling. It will also free up development time."]}),"\n",(0,o.jsx)(s.p,{children:"If you are currently using the PouchDB RxStorage, you have these options:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:["Migrate to another ",(0,o.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," (recommended)"]}),"\n",(0,o.jsx)(s.li,{children:"Never update RxDB to the next major version (stay on older 14.0.0)"}),"\n",(0,o.jsxs)(s.li,{children:["Fork the ",(0,o.jsx)(s.a,{href:"/rx-storage-pouchdb.html",children:"PouchDB RxStorage"})," and maintain the plugin by yourself."]}),"\n",(0,o.jsxs)(s.li,{children:["Fix all the ",(0,o.jsx)(s.a,{href:"https://github.com/pouchdb/pouchdb/issues?q=author%3Apubkey",children:"PouchDB problems"})," so that we can add PouchDB to the RxDB Core again."]}),"\n"]}),"\n",(0,o.jsx)(s.h2,{id:"pros",children:"Pros"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsx)(s.li,{children:"Most battle proven RxStorage"}),"\n",(0,o.jsx)(s.li,{children:"Supports replication with a CouchDB endpoint"}),"\n",(0,o.jsxs)(s.li,{children:["Support storing ",(0,o.jsx)(s.a,{href:"/rx-attachment.html",children:"attachments"})]}),"\n",(0,o.jsx)(s.li,{children:"Big ecosystem of adapters"}),"\n"]}),"\n",(0,o.jsx)(s.h2,{id:"cons",children:"Cons"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsx)(s.li,{children:"Big bundle size"}),"\n",(0,o.jsx)(s.li,{children:"Slow performance because of revision handling overhead"}),"\n"]}),"\n",(0,o.jsx)(s.h2,{id:"usage",children:"Usage"}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStoragePouch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" addPouchPlugin } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/pouchdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-idb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'idb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * other pouchdb specific options"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" https://pouchdb.com/api.html#create_database"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsxs)(s.h2,{id:"polyfill-the-global-variable",children:["Polyfill the ",(0,o.jsx)(s.code,{children:"global"})," variable"]}),"\n",(0,o.jsxs)(s.p,{children:["When you use RxDB with ",(0,o.jsx)(s.strong,{children:"angular"})," or other ",(0,o.jsx)(s.strong,{children:"webpack"})," based frameworks, you might get the error:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"html","data-theme":"css-variables",children:(0,o.jsx)(s.code,{"data-language":"html","data-theme":"css-variables",style:{display:"grid"},children:(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" style"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"color: red;"'}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">Uncaught ReferenceError: global is not defined"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"span"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]})})})}),"\n",(0,o.jsxs)(s.p,{children:["This is because pouchdb assumes a nodejs-specific ",(0,o.jsx)(s.code,{children:"global"})," variable that is not added to browser runtimes by some bundlers.\nYou have to add them by your own, like we do ",(0,o.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/master/examples/angular/src/polyfills.ts",children:"here"}),"."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(window "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:").global "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" window;"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(window "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:").process "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" env"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { DEBUG"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" undefined"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"adapters",children:"Adapters"}),"\n",(0,o.jsxs)(s.p,{children:[(0,o.jsx)(s.a,{href:"/adapters.html",children:"PouchDB has many adapters for all JavaScript runtimes"}),"."]}),"\n",(0,o.jsx)(s.h2,{id:"using-the-internal-pouchdb-database",children:"Using the internal PouchDB Database"}),"\n",(0,o.jsx)(s.p,{children:"For custom operations, you can access the internal PouchDB database.\nThis is dangerous because you might do changes that are not compatible with RxDB.\nOnly use this when there is no way to achieve your goals via the RxDB API."}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getPouchDBOfRxCollection"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/pouchdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pouch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getPouchDBOfRxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(myRxCollection);"})]})]})})})]})}function d(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(c,{...e})}):c(e)}},8453(e,s,r){r.d(s,{R:()=>i,x:()=>t});var n=r(6540);const o={},a=n.createContext(o);function i(e){const s=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),n.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4af60d2e.8fe42f95.js b/docs/assets/js/4af60d2e.8fe42f95.js
deleted file mode 100644
index 6518c2c01a8..00000000000
--- a/docs/assets/js/4af60d2e.8fe42f95.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8545],{6136(e,s,a){a.r(s),a.d(s,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"articles/realtime-database","title":"What Really Is a Realtime Database?","description":"Discover how RxDB merges realtime replication and dynamic updates to deliver seamless data sync across browsers, devices, and servers - instantly.","source":"@site/docs/articles/realtime-database.md","sourceDirName":"articles","slug":"/articles/realtime-database.html","permalink":"/articles/realtime-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"What Really Is a Realtime Database?","slug":"realtime-database.html","description":"Discover how RxDB merges realtime replication and dynamic updates to deliver seamless data sync across browsers, devices, and servers - instantly."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB as a Database for React Applications","permalink":"/articles/react-database.html"},"next":{"title":"Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone","permalink":"/articles/angular-indexeddb.html"}}');var t=a(4848),i=a(8453);const r={title:"What Really Is a Realtime Database?",slug:"realtime-database.html",description:"Discover how RxDB merges realtime replication and dynamic updates to deliver seamless data sync across browsers, devices, and servers - instantly."},l="What is a realtime database?",o={},c=[{value:"Realtime as in realtime computing",id:"realtime-as-in-realtime-computing",level:2},{value:"Real time Database as in realtime replication",id:"real-time-database-as-in-realtime-replication",level:2},{value:"Realtime as in realtime applications",id:"realtime-as-in-realtime-applications",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(s.header,{children:(0,t.jsx)(s.h1,{id:"what-is-a-realtime-database",children:"What is a realtime database?"})}),"\n",(0,t.jsxs)(s.p,{children:["I have been building ",(0,t.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"}),", a NoSQL ",(0,t.jsx)(s.strong,{children:"realtime"})," JavaScript database for many years.\nOften people get confused by the word ",(0,t.jsx)(s.strong,{children:"realtime database"}),", because the word ",(0,t.jsx)(s.strong,{children:"realtime"})," is so vaguely defined that it can mean everything and nothing at the same time."]}),"\n",(0,t.jsx)(s.p,{children:"In this article we will explore what a realtime database is, and more important, what it is not."}),"\n",(0,t.jsx)("center",{children:(0,t.jsx)("a",{href:"https://rxdb.info/",children:(0,t.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Realtime Database",width:"150"})})}),"\n",(0,t.jsxs)(s.h2,{id:"realtime-as-in-realtime-computing",children:["Realtime as in ",(0,t.jsx)(s.strong,{children:"realtime computing"})]}),"\n",(0,t.jsxs)(s.p,{children:['When "normal" developers hear the word "realtime", they think of ',(0,t.jsx)(s.strong,{children:"Real-time computing (RTC)"}),". Real-time computing is a type of computer processing that ",(0,t.jsx)(s.strong,{children:"guarantees specific response times"})," for tasks or events, crucial in applications like industrial control, automotive systems, and aerospace. It relies on specialized operating systems (RTOS) to ensure predictability and low latency. Hard real-time systems must never miss deadlines, while soft real-time systems can tolerate occasional delays. Real-time responses are often understood to be in the order of milliseconds, and sometimes microseconds."]}),"\n",(0,t.jsxs)(s.p,{children:["Consider the role of real-time computing in car airbags: sensors detect collision force, swiftly process the data, and immediately decide to deploy the airbags within milliseconds. Such rapid action is imperative for safeguarding passengers. Hence, the controlling chip must ",(0,t.jsx)(s.strong,{children:"guarantee a certain response time"}),' - it must operate in "realtime".']}),"\n",(0,t.jsxs)(s.p,{children:["But when people talk about ",(0,t.jsx)(s.strong,{children:"realtime databases"}),", especially in the web-development world, they almost never mean realtime, as in ",(0,t.jsx)(s.strong,{children:"realtime computing"}),', they mean something else.\nIn fact, with any programming language that run on end users devices, it is not even possible to built a "real" realtime database. A program, like a JavaScript (',(0,t.jsx)(s.a,{href:"/articles/browser-database.html",children:"browser"})," or ",(0,t.jsx)(s.a,{href:"/nodejs-database.html",children:"Node.js"}),") process, can be halted by the operating systems task manager at any time and therefore it will never be able to guarantee specific response times. To build a realtime computing database, you would need a realtime capable operating system."]}),"\n",(0,t.jsxs)(s.h2,{id:"real-time-database-as-in-realtime-replication",children:["Real time Database as in ",(0,t.jsx)(s.strong,{children:"realtime replication"})]}),"\n",(0,t.jsxs)(s.p,{children:["When talking about realtime databases, most people refer to realtime, as in realtime replication.\nOften they mean a very specific product which is the ",(0,t.jsx)(s.strong,{children:"Firebase Realtime Database"})," (not the ",(0,t.jsx)(s.a,{href:"/replication-firestore.html",children:"Firestore"}),")."]}),"\n",(0,t.jsx)("p",{align:"center",children:(0,t.jsx)("img",{src:"../files/alternatives/firebase.svg",alt:"firebase realtime replication",width:"100"})}),"\n",(0,t.jsx)(s.p,{children:'In the context of the Firebase Realtime Database, "realtime" means that data changes are synchronized and delivered to all connected clients or devices as soon as they occur, typically within milliseconds. This means that when any client updates, adds, or removes data in the database, all other clients that are connected to the same database instance receive those updates instantly, without the need for manual polling or frequent HTTP requests.'}),"\n",(0,t.jsxs)(s.p,{children:["In short, when replicating data between databases, instead of polling, we use a ",(0,t.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API",children:"websocket connection"}),' to live-stream all changes between the server and the clients, this is labeled as "realtime database". A similar thing can be done with RxDB and the ',(0,t.jsx)(s.a,{href:"/replication.html",children:"RxDB Replication Plugins"}),"."]}),"\n",(0,t.jsx)("p",{align:"center",children:(0,t.jsx)("a",{href:"https://rxdb.info/replication.html",children:(0,t.jsx)("img",{src:"../files/database-replication.png",alt:"database replication",width:"100"})})}),"\n",(0,t.jsxs)(s.h2,{id:"realtime-as-in-realtime-applications",children:["Realtime as in ",(0,t.jsx)(s.strong,{children:"realtime applications"})]}),"\n",(0,t.jsx)(s.p,{children:'In the context of realtime client-side applications, "realtime" refers to the immediate or near-instantaneous processing and response to events or data inputs. When data changes, the application must directly update to reflect the new data state, without any user interaction or delay. Notice that the change to the data could have come from any source, like a user action, an operation in another browser tab, or even an operation from another device that has been replicated to the client.'}),"\n",(0,t.jsx)("p",{align:"center",children:(0,t.jsx)("img",{src:"../files/multiwindow.gif",alt:"realtime applications",width:"400"})}),"\n",(0,t.jsxs)(s.p,{children:["In contrast to push-pull based databases (e.g., MySQL or MongoDB servers), a realtime database contains ",(0,t.jsx)(s.strong,{children:"features which make it easy to build realtime applications"}),". For example with RxDB you can not only fetch query results once, but instead you can subscribe to a query and directly update the HTML dom tree whenever the query has a new result set:"]}),"\n",(0,t.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,t.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,t.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" healthpoints"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"})]}),"\n",(0,t.jsx)(s.span,{"data-line":"",children:(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,t.jsx)(s.span,{"data-line":"",children:(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,t.jsx)(s.span,{"data-line":"",children:(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".$ "}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// The $ returns an observable that emits whenever the query's result set changes."})]}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(aliveHeroes "}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsx)(s.span,{"data-line":"",children:(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Refresh the HTML list each time there are new query results."})}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" newContent"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" aliveHeroes"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '
'"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" document"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getElementById"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'#myList'"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:").innerHTML "}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" newContent;"})]}),"\n",(0,t.jsx)(s.span,{"data-line":"",children:(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,t.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,t.jsx)(s.span,{"data-line":"",children:(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// You can even subscribe to any RxDB document's fields."})}),"\n",(0,t.jsxs)(s.span,{"data-line":"",children:[(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDocument"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"firstName$"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(newName "}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'name is: '"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,t.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" newName));"})]})]})})}),"\n",(0,t.jsxs)(s.p,{children:["A competent realtime application is engineered to offer feedback or results swiftly, ideally within milliseconds to microseconds. Ideally, a data modification should be processed in under ",(0,t.jsx)(s.strong,{children:"16 milliseconds"})," (since 1 second divided by 60 frames equals 16.66ms) to ensure users don't perceive any lag from input to visualization. RxDB utilizes the ",(0,t.jsx)(s.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce algorithm"}),' to manage changes more swiftly than 16ms. However, it can never assure fixed response times as a "realtime computing database" would.']}),"\n",(0,t.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,t.jsxs)(s.ul,{children:["\n",(0,t.jsxs)(s.li,{children:["Dive into the ",(0,t.jsx)(s.a,{href:"https://rxdb.info/quickstart.html",children:"RxDB Quickstart"})]}),"\n",(0,t.jsxs)(s.li,{children:["Discover more about the ",(0,t.jsx)(s.a,{href:"/replication.html",children:"RxDB realtime Sync Engine"})]}),"\n",(0,t.jsxs)(s.li,{children:["Join the conversation at ",(0,t.jsx)(s.a,{href:"https://rxdb.info/chat/",children:"RxDB Chat"})]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,t.jsx)(s,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}},8453(e,s,a){a.d(s,{R:()=>r,x:()=>l});var n=a(6540);const t={},i=n.createContext(t);function r(e){const s=n.useContext(i);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),n.createElement(i.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4ba7e5a3.fec476c6.js b/docs/assets/js/4ba7e5a3.fec476c6.js
deleted file mode 100644
index 9ebc3d7b19d..00000000000
--- a/docs/assets/js/4ba7e5a3.fec476c6.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9591],{5380(e,t,n){n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>a,default:()=>h,frontMatter:()=>s,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"contribute","title":"Contribute","description":"Got a fix or fresh idea? Learn how to contribute to RxDB, run tests, and shape the future of this cutting-edge NoSQL database for JavaScript.","source":"@site/docs/contribute.md","sourceDirName":".","slug":"/contribution.html","permalink":"/contribution.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Contribute","slug":"contribution.html","description":"Got a fix or fresh idea? Learn how to contribute to RxDB, run tests, and shape the future of this cutting-edge NoSQL database for JavaScript."},"sidebar":"tutorialSidebar","previous":{"title":"ReactJS Storage - From Basic LocalStorage to Advanced Offline Apps with RxDB","permalink":"/articles/reactjs-storage.html"}}');var o=n(4848),r=n(8453);const s={title:"Contribute",slug:"contribution.html",description:"Got a fix or fresh idea? Learn how to contribute to RxDB, run tests, and shape the future of this cutting-edge NoSQL database for JavaScript."},a="Contribution",d={},c=[{value:"Requirements",id:"requirements",level:2},{value:"Adding tests",id:"adding-tests",level:2},{value:"Making a PR",id:"making-a-pr",level:2},{value:"Getting help",id:"getting-help",level:2},{value:"No-Go",id:"no-go",level:2}];function l(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",...(0,r.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.header,{children:(0,o.jsx)(t.h1,{id:"contribution",children:"Contribution"})}),"\n",(0,o.jsx)(t.p,{children:"We are open to, and grateful for, any contributions made by the community."}),"\n",(0,o.jsx)(t.h1,{id:"developing",children:"Developing"}),"\n",(0,o.jsx)(t.h2,{id:"requirements",children:"Requirements"}),"\n",(0,o.jsx)(t.p,{children:"Before you can start developing, do the following:"}),"\n",(0,o.jsxs)(t.ol,{children:["\n",(0,o.jsxs)(t.li,{children:["Make sure you have installed nodejs with the version stated in the ",(0,o.jsx)(t.a,{href:"https://github.com/pubkey/rxdb/blob/master/.nvmrc",children:".nvmrc"})]}),"\n",(0,o.jsxs)(t.li,{children:["Clone the repository ",(0,o.jsx)(t.code,{children:"git clone https://github.com/pubkey/rxdb.git"})]}),"\n",(0,o.jsxs)(t.li,{children:["Install the dependencies ",(0,o.jsx)(t.code,{children:"cd rxdb && npm install"})]}),"\n",(0,o.jsxs)(t.li,{children:["Make sure that the tests work for you. At first, try it out with ",(0,o.jsx)(t.code,{children:"npm run test:node:memory"})," which tests the memory storage in node. In the ",(0,o.jsx)(t.a,{href:"https://github.com/pubkey/rxdb/blob/master/package.json",children:"package.json"})," you can find more scripts to run the tests with different storages."]}),"\n"]}),"\n",(0,o.jsx)(t.h2,{id:"adding-tests",children:"Adding tests"}),"\n",(0,o.jsxs)(t.p,{children:["Before you start creating a bugfix or a feature, you should create a test to reproduce it. Tests are in the ",(0,o.jsx)(t.code,{children:"test/unit"}),"-folder.\nIf you want to reproduce a bug, you can modify the test in ",(0,o.jsx)(t.a,{href:"https://github.com/pubkey/rxdb/blob/master/test/unit/bug-report.test.ts",children:"this file"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"making-a-pr",children:"Making a PR"}),"\n",(0,o.jsx)(t.p,{children:"If you make a pull-request, ensure the following:"}),"\n",(0,o.jsxs)(t.ol,{children:["\n",(0,o.jsx)(t.li,{children:"Every feature or bugfix must be committed together with a unit-test which ensures everything works as expected."}),"\n",(0,o.jsxs)(t.li,{children:["Do not commit build-files (anything in the ",(0,o.jsx)(t.code,{children:"dist"}),"-folder)"]}),"\n",(0,o.jsx)(t.li,{children:"Before you add non-trivial changes, create an issue to discuss if this will be merged and you don't waste your time."}),"\n",(0,o.jsxs)(t.li,{children:["To run the unit and integration-tests, do ",(0,o.jsx)(t.code,{children:"npm run test"})," and ensure everything works as expected"]}),"\n"]}),"\n",(0,o.jsx)(t.h2,{id:"getting-help",children:"Getting help"}),"\n",(0,o.jsxs)(t.p,{children:["If you need help with your contribution, ask at ",(0,o.jsx)(t.a,{href:"https://rxdb.info/chat/",children:"discord"}),"."]}),"\n",(0,o.jsx)(t.h2,{id:"no-go",children:"No-Go"}),"\n",(0,o.jsx)(t.p,{children:"When reporting a bug, you need to make a PR with a test case that runs in the CI and reproduces your problem.\nSending a link with a repo does not help the maintainer because installing random peoples projects is time consuming and dangerous.\nAlso the maintainer will never go on a bug hunt based on your plain description. Either you report the bug with a test case, or the maintainer will likely not help you."}),"\n",(0,o.jsx)(t.h1,{id:"docs",children:"Docs"}),"\n",(0,o.jsxs)(t.p,{children:["The source of the documentation is at the ",(0,o.jsx)(t.code,{children:"docs-src"}),"-folder.\nTo read the docs locally, run ",(0,o.jsx)(t.code,{children:"npm run docs:install && npm run docs:serve"})," and open ",(0,o.jsx)(t.code,{children:"http://localhost:4000/"})]}),"\n",(0,o.jsx)(t.h1,{id:"thank-you-for-contributing",children:"Thank you for contributing!"})]})}function h(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(l,{...e})}):l(e)}},8453(e,t,n){n.d(t,{R:()=>s,x:()=>a});var i=n(6540);const o={},r=i.createContext(o);function s(e){const t=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),i.createElement(r.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4ed9495b.b85648e1.js b/docs/assets/js/4ed9495b.b85648e1.js
deleted file mode 100644
index 6d0dc2eef67..00000000000
--- a/docs/assets/js/4ed9495b.b85648e1.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1199],{3247(e,s,r){r.d(s,{g:()=>i});var n=r(4848);function i(e){const s=[];let r=null;return e.children.forEach(e=>{e.props.id?(r&&s.push(r),r={headline:e,paragraphs:[]}):r&&r.paragraphs.push(e)}),r&&s.push(r),(0,n.jsx)("div",{style:a.stepsContainer,children:s.map((e,s)=>(0,n.jsxs)("div",{style:a.stepWrapper,children:[(0,n.jsxs)("div",{style:a.stepIndicator,children:[(0,n.jsx)("div",{style:a.stepNumber,children:s+1}),(0,n.jsx)("div",{style:a.verticalLine})]}),(0,n.jsxs)("div",{style:a.stepContent,children:[(0,n.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,n.jsx)("div",{style:a.item,children:e},s))]})]},s))})}const a={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5356(e,s,r){r.r(s),r.d(s,{assets:()=>c,contentTitle:()=>t,default:()=>p,frontMatter:()=>l,metadata:()=>n,toc:()=>d});const n=JSON.parse('{"id":"rx-storage-sqlite","title":"RxDB SQLite RxStorage for Hybrid Apps","description":"Unlock seamless persistence with SQLite RxStorage. Explore usage in hybrid apps, compare performance, and leverage advanced features like attachments.","source":"@site/docs/rx-storage-sqlite.md","sourceDirName":".","slug":"/rx-storage-sqlite.html","permalink":"/rx-storage-sqlite.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB SQLite RxStorage for Hybrid Apps","slug":"rx-storage-sqlite.html","description":"Unlock seamless persistence with SQLite RxStorage. Explore usage in hybrid apps, compare performance, and leverage advanced features like attachments."},"sidebar":"tutorialSidebar","previous":{"title":"Filesystem Node \ud83d\udc51 (Node.js)","permalink":"/rx-storage-filesystem-node.html"},"next":{"title":"Dexie.js","permalink":"/rx-storage-dexie.html"}}');var i=r(4848),a=r(8453),o=(r(3247),r(2636));const l={title:"RxDB SQLite RxStorage for Hybrid Apps",slug:"rx-storage-sqlite.html",description:"Unlock seamless persistence with SQLite RxStorage. Explore usage in hybrid apps, compare performance, and leverage advanced features like attachments."},t="SQLite RxStorage",c={},d=[{value:"Performance comparison with other storages",id:"performance-comparison-with-other-storages",level:2},{value:"Using the SQLite RxStorage",id:"using-the-sqlite-rxstorage",level:2},{value:"Trial Version",id:"trial-version",level:2},{value:"RxDB Premium \ud83d\udc51",id:"rxdb-premium-",level:2},{value:"SQLiteBasics",id:"sqlitebasics",level:2},{value:"Using the SQLite RxStorage with different SQLite libraries",id:"using-the-sqlite-rxstorage-with-different-sqlite-libraries",level:2},{value:"Usage with the sqlite3 npm package",id:"usage-with-the-sqlite3-npm-package",level:3},{value:"Usage with the node package",id:"usage-with-the-node-package",level:3},{value:"Usage with Webassembly in the Browser",id:"usage-with-webassembly-in-the-browser",level:3},{value:"Usage with React Native",id:"usage-with-react-native",level:3},{value:"Usage with Expo SQLite",id:"usage-with-expo-sqlite",level:3},{value:"Usage with SQLite Capacitor",id:"usage-with-sqlite-capacitor",level:3},{value:"Usage with Tauri SQLite",id:"usage-with-tauri-sqlite",level:3},{value:"Database Connection",id:"database-connection",level:2},{value:"Known Problems of SQLite in JavaScript apps",id:"known-problems-of-sqlite-in-javascript-apps",level:2},{value:"Related",id:"related",level:2}];function h(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"sqlite-rxstorage",children:"SQLite RxStorage"})}),"\n",(0,i.jsxs)(s.p,{children:["This ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," is based on ",(0,i.jsx)(s.a,{href:"https://www.sqlite.org/index.html",children:"SQLite"})," and is made to work with ",(0,i.jsx)(s.strong,{children:"Node.js"}),", ",(0,i.jsx)(s.a,{href:"/electron-database.html",children:"Electron"}),", ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"})," and ",(0,i.jsx)(s.a,{href:"/capacitor-database.html",children:"Capacitor"})," or SQLite via webassembly in the browser. It can be used with different so called ",(0,i.jsx)(s.code,{children:"sqliteBasics"})," adapters to account for the differences in the various SQLite bundles and libraries that exist."]}),"\n",(0,i.jsxs)(s.p,{children:["SQLite is a natural fit for RxDB because most platforms - Android, iOS, Node.js, and beyond - already ship with a built-in SQLite engine, delivering robust performance and minimal setup overhead. Its proven reliability, having powered countless applications over the years, ensures a battle-tested foundation for local data. By placing RxDB on top of SQLite, you gain advanced features suited for building interactive, offline-capable UI apps: ",(0,i.jsx)(s.a,{href:"/rx-query.html#observe",children:"real-time queries"}),", reactive state updates, ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"conflict handling"}),", ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"data encryption"}),", and straightforward ",(0,i.jsx)(s.a,{href:"/rx-schema.html",children:"schema management"}),". This combination offers a unified NoSQL-like experience without sacrificing the speed and broad availability that SQLite brings."]}),"\n",(0,i.jsx)(s.h2,{id:"performance-comparison-with-other-storages",children:"Performance comparison with other storages"}),"\n",(0,i.jsxs)(s.p,{children:["The SQLite storage is a bit slower compared to other Node.js based storages like the ",(0,i.jsx)(s.a,{href:"/rx-storage-filesystem-node.html",children:"Filesystem Storage"})," because wrapping SQLite has a bit of overhead and sending data from the JavaScript process to SQLite and backwards increases the latency. However for most hybrid apps the SQLite storage is the best option because it can leverage the SQLite version that comes already installed on the smartphones OS (iOS and android). Also for desktop electron apps it can be a viable solution because it is easy to ship SQLite together inside of the electron bundle."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/rx-storage-performance-node.png",alt:"SQLite performance - Node.js",width:"700"})}),"\n",(0,i.jsx)(s.h2,{id:"using-the-sqlite-rxstorage",children:"Using the SQLite RxStorage"}),"\n",(0,i.jsx)(s.p,{children:"There are two versions of the SQLite storage available for RxDB:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.strong,{children:"trial version"})," which comes directly shipped with RxDB Core. It contains an SQLite storage that allows you to try out RxDB on devices that support SQLite, like React Native or Electron. While the trial version does pass the full RxDB storage test-suite, it is not made for production. It is not using indexes, has no attachment support, is limited to store 300 documents and fetches the whole storage state to run queries in memory. ",(0,i.jsx)(s.strong,{children:"Use it for evaluation and prototypes only!"})]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsxs)(s.strong,{children:[(0,i.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"})," version"]})," which contains the full production-ready SQLite storage. It contains a full load of performance optimizations and full query support. To use the SQLite storage you have to import ",(0,i.jsx)(s.code,{children:"getRxStorageSQLite"})," from the ",(0,i.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"})," package and then add the correct ",(0,i.jsx)(s.code,{children:"sqliteBasics"})," adapter depending on which sqlite module you want to use. This can then be used as storage when creating the ",(0,i.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"}),". In the following you can see some examples for some of the most common SQLite packages."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(o.t,{children:[(0,i.jsx)(s.h2,{id:"trial-version",children:"Trial Version"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Import the Trial SQLite Storage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLiteTrial"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsNodeNative"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create a Storage for it, here we use the nodejs-native SQLite module"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// other SQLite modules can be used with a different sqliteBasics adapter"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { DatabaseSync } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'node:sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLiteTrial"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsNodeNative"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(DatabaseSync)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create a Database with the Storage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h2,{id:"rxdb-premium-",children:"RxDB Premium \ud83d\udc51"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Import the SQLite Storage from the premium plugins."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsNodeNative"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create a Storage for it, here we use the nodejs-native SQLite module"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// other SQLite modules can be used with a different sqliteBasics adapter"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { DatabaseSync } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'node:sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsNodeNative"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(DatabaseSync)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create a Database with the Storage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]}),"\n",(0,i.jsx)(s.p,{children:"In the following, all examples are shown with the premium SQLite storage. Still they work the same with the trial version."}),"\n",(0,i.jsx)(s.h2,{id:"sqlitebasics",children:"SQLiteBasics"}),"\n",(0,i.jsxs)(s.p,{children:["Different SQLite libraries have different APIs to create and access the SQLite database. Therefore the library must be massaged to work with the RxDB SQlite storage. This is done in a so called ",(0,i.jsx)(s.code,{children:"SQLiteBasics"})," interface. RxDB directly ships with a wide range of these for various SQLite libraries that are commonly used. Also creating your own one is pretty simple, check the source code of the existing ones for that."]}),"\n",(0,i.jsxs)(s.p,{children:["For example for the ",(0,i.jsx)(s.code,{children:"sqlite3"})," npm library we have the ",(0,i.jsx)(s.code,{children:"getSQLiteBasicsNode()"})," implementation. For ",(0,i.jsx)(s.code,{children:"node:sqlite"})," we have the ",(0,i.jsx)(s.code,{children:"getSQLiteBasicsNodeNative()"})," implementation and so on.."]}),"\n",(0,i.jsx)(s.h2,{id:"using-the-sqlite-rxstorage-with-different-sqlite-libraries",children:"Using the SQLite RxStorage with different SQLite libraries"}),"\n",(0,i.jsxs)(s.h3,{id:"usage-with-the-sqlite3-npm-package",children:["Usage with the ",(0,i.jsx)(s.strong,{children:"sqlite3 npm package"})]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsNode"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * In Node.js, we use the SQLite database"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * from the 'sqlite' npm module."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" https://www.npmjs.com/package/sqlite3"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqlite3 "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'sqlite3'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Different runtimes have different interfaces to SQLite."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * For example in node.js we have a callback API,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * while in capacitor sqlite we have Promises."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * So we need a helper object that is capable of doing the basic"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * sqlite operations."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsNode"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite3)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.h3,{id:"usage-with-the-node-package",children:["Usage with the ",(0,i.jsxs)(s.strong,{children:["node",":sqlite"]})," package"]}),"\n",(0,i.jsxs)(s.p,{children:['With Node.js version 22 and newer, you can use the "native" ',(0,i.jsx)(s.a,{href:"https://nodejs.org/api/sqlite.html",children:"sqlite module"})," that comes shipped with Node.js."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsNodeNative"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { DatabaseSync } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'node:sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsNodeNative"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(DatabaseSync)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"usage-with-webassembly-in-the-browser",children:"Usage with Webassembly in the Browser"}),"\n",(0,i.jsxs)(s.p,{children:["In the browser you can use the ",(0,i.jsx)(s.a,{href:"https://github.com/rhashimoto/wa-sqlite",children:"wa-sqlite"})," package to run sQLite in Webassembly. The wa-sqlite module also allows to use persistence with IndexedDB or OPFS. Notice that in general SQLite via Webassembly is slower compared to other storages like ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," or ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," because sending data from the main thread to wasm and backwards is slow in the browser. Have a look the ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance comparison"}),"."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsWasm"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * In the Browser, we use the SQLite database"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * from the 'wa-sqlite' npm module. This contains the SQLite library"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * compiled to Webassembly"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" https://www.npmjs.com/package/wa-sqlite"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLiteESMFactory "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'wa-sqlite/dist/wa-sqlite-async.mjs'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLite "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'wa-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sqliteModule"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" SQLiteESMFactory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sqlite3"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" SQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".Factory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"module"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsWasm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite3)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.h3,{id:"usage-with-react-native",children:["Usage with ",(0,i.jsx)(s.strong,{children:"React Native"})]}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:["Install the ",(0,i.jsx)(s.a,{href:"https://www.npmjs.com/package/react-native-quick-sqlite",children:"react-native-quick-sqlite npm module"})]}),"\n",(0,i.jsxs)(s.li,{children:["Import ",(0,i.jsx)(s.code,{children:"getSQLiteBasicsQuickSQLite"})," from the SQLite plugin and use it to create a ",(0,i.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"}),":"]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsQuickSQLite"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { open } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'react-native-quick-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create database"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- Set multiInstance to false when using RxDB in React Native"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsQuickSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(open)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["If ",(0,i.jsx)(s.code,{children:"react-native-quick-sqlite"})," does not work for you, as alternative you can use the ",(0,i.jsx)(s.a,{href:"https://www.npmjs.com/package/react-native-sqlite-2",children:"react-native-sqlite-2"})," library instead:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsWebSQL"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLite "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'react-native-sqlite-2'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsWebSQL"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"SQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".openDatabase)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.h3,{id:"usage-with-expo-sqlite",children:["Usage with ",(0,i.jsx)(s.strong,{children:"Expo SQLite"})]}),"\n",(0,i.jsxs)(s.p,{children:["Notice that ",(0,i.jsx)(s.a,{href:"https://www.npmjs.com/package/expo-sqlite",children:"expo-sqlite"})," cannot be used on android (but it works on iOS) if you use Expo SDK version 50 or older. Please update to Version 50 or newer to use it."]}),"\n",(0,i.jsxs)(s.p,{children:["In the latest expo SDK version, use the ",(0,i.jsx)(s.code,{children:"getSQLiteBasicsExpoSQLiteAsync()"})," method:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsExpoSQLiteAsync"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" *"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" as"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLite "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'expo-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsExpoSQLiteAsync"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"SQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".openDatabaseAsync)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"In older Expo SDK versions, you might have to use the non-async API:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsExpoSQLite"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { openDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'expo-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsExpoSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(openDatabase)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.h3,{id:"usage-with-sqlite-capacitor",children:["Usage with ",(0,i.jsx)(s.strong,{children:"SQLite Capacitor"})]}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:["Install the ",(0,i.jsx)(s.a,{href:"https://github.com/capacitor-community/sqlite",children:"sqlite capacitor npm module"})]}),"\n",(0,i.jsx)(s.li,{children:"Add the iOS database location to your capacitor config"}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "plugins"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "CapacitorSQLite"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "iosDatabaseLocation"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Library/CapacitorDatabase"'})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(s.ol,{start:"3",children:["\n",(0,i.jsxs)(s.li,{children:["Use the function ",(0,i.jsx)(s.code,{children:"getSQLiteBasicsCapacitor"})," to get the capacitor sqlite wrapper."]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsCapacitor"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Import SQLite from the capacitor plugin."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" CapacitorSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLiteConnection"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@capacitor-community/sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Capacitor } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@capacitor/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" SQLiteConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(CapacitorSQLite);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Different runtimes have different interfaces to SQLite."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * For example in node.js we have a callback API,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * while in capacitor sqlite we have Promises."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * So we need a helper object that is capable of doing the basic"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * sqlite operations."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsCapacitor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Capacitor)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"usage-with-tauri-sqlite",children:"Usage with Tauri SQLite"}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:["Add the ",(0,i.jsx)(s.a,{href:"https://tauri.app/plugin/sql/#setup",children:"Tauri SQL plugin"})," to your Tauri project."]}),"\n",(0,i.jsxs)(s.li,{children:["Make sure to add ",(0,i.jsx)(s.code,{children:"sqlite"})," as your database engine by running ",(0,i.jsx)(s.code,{children:"cargo add tauri-plugin-sql --features sqlite"})," inside ",(0,i.jsx)(s.code,{children:"src-tauri"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["Use the ",(0,i.jsx)(s.code,{children:"getSQLiteBasicsTauri"})," function to get the Tauri SQLite wrapper."]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsTauri"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqlite3 "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@tauri-apps/plugin-sql'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsTauri"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite3)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"database-connection",children:"Database Connection"}),"\n",(0,i.jsxs)(s.p,{children:["If you need to access the database connection for any reason you can use ",(0,i.jsx)(s.code,{children:"getDatabaseConnection"})," to do so:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getDatabaseConnection } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"})]})})})}),"\n",(0,i.jsx)(s.p,{children:"It has the following signature:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"getDatabaseConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics: SQLiteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" databaseName: string"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"): "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"SQLiteDatabaseClass"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"known-problems-of-sqlite-in-javascript-apps",children:"Known Problems of SQLite in JavaScript apps"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:["Some JavaScript runtimes do not contain a ",(0,i.jsx)(s.code,{children:"Buffer"})," API which is used by SQLite to store binary attachments data as ",(0,i.jsx)(s.code,{children:"BLOB"}),". You can set ",(0,i.jsx)(s.code,{children:"storeAttachmentsAsBase64String: true"})," if you want to store the attachments data as base64 string instead. This increases the database size but makes it work even without having a ",(0,i.jsx)(s.code,{children:"Buffer"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:["The SQlite RxStorage works on SQLite libraries that use SQLite in version ",(0,i.jsx)(s.code,{children:"3.38.0 (2022-02-22)"})," or newer, because it uses the ",(0,i.jsx)(s.a,{href:"https://www.sqlite.org/json1.html",children:"SQLite JSON"})," methods like ",(0,i.jsx)(s.code,{children:"JSON_EXTRACT"}),". If you get an error like ",(0,i.jsx)(s.code,{children:"[Error: no such function: JSON_EXTRACT (code 1 SQLITE_ERROR[1])"}),", you might have a too old version of SQLite."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:["To debug all SQL operations, you can pass a log function to ",(0,i.jsx)(s.code,{children:"getRxStorageSQLite()"})," like this. This does not work with the trial version:"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsCapacitor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Capacitor)"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // pass log function"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".bind"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(console)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["By default, all tables will be created with the ",(0,i.jsx)(s.code,{children:"WITHOUT ROWID"})," flag. Some tools like drizzle do not support tables with that option. You can disable it by setting ",(0,i.jsx)(s.code,{children:"withoutRowId: false"})," when calling ",(0,i.jsx)(s.code,{children:"getRxStorageSQLite()"}),":"]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsCapacitor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Capacitor)"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" withoutRowId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"related",children:"Related"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native Databases"})}),"\n"]})]})}function p(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,s,r){r.d(s,{R:()=>o,x:()=>l});var n=r(6540);const i={},a=n.createContext(i);function o(e){const s=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),n.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/4f17bbdd.b46c3eaf.js b/docs/assets/js/4f17bbdd.b46c3eaf.js
deleted file mode 100644
index a2ca88ad4b7..00000000000
--- a/docs/assets/js/4f17bbdd.b46c3eaf.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2373],{1206(e,i,s){s.r(i),s.d(i,{default:()=>n});var r=s(4586),l=s(8711),t=s(6540),c=s(8141),d=s(4848);function n(){const{siteConfig:e}=(0,r.A)();return(0,t.useEffect)(()=>{(0,c.c)("paid-meeting-link-clicked",100,1)}),(0,d.jsx)(l.A,{title:`Meeting - ${e.title}`,description:"RxDB Meeting Scheduler",children:(0,d.jsx)("main",{children:(0,d.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,d.jsx)("a",{href:"/",children:(0,d.jsx)("div",{className:"logo",children:(0,d.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,d.jsx)("h1",{children:"RxDB Meeting Scheduler"}),(0,d.jsx)("p",{children:(0,d.jsx)("b",{children:"You will be redirected in a few seconds."})}),(0,d.jsx)("p",{children:(0,d.jsx)("a",{href:"https://rxdb.pipedrive.com/scheduler/Z6A0M1sr/rxdb-1h-paid-consulting-session",children:"Click here"})}),(0,d.jsx)("meta",{httpEquiv:"Refresh",content:"0; url=https://rxdb.pipedrive.com/scheduler/Z6A0M1sr/rxdb-1h-paid-consulting-session"})]})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/502d8946.3c4d7aab.js b/docs/assets/js/502d8946.3c4d7aab.js
deleted file mode 100644
index 131b9122d64..00000000000
--- a/docs/assets/js/502d8946.3c4d7aab.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7817],{732(e,n,i){i.r(n),i.d(n,{default:()=>h});var r=i(4586),t=i(5260),s=i(8711),l=(i(6540),i(4848));function h(){const{siteConfig:e}=(0,r.A)();return(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(t.A,{children:(0,l.jsx)("meta",{name:"robots",content:"noindex"})}),(0,l.jsx)(s.A,{title:`Legal Notice - ${e.title}`,description:"RxDB Legal Notice",children:(0,l.jsxs)("main",{children:[(0,l.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,l.jsx)("a",{href:"/",children:(0,l.jsx)("div",{className:"logo",children:(0,l.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,l.jsxs)("h1",{children:[(0,l.jsx)("a",{href:"https://rxdb.info/",children:"RxDB"})," Legal Notice"]}),(0,l.jsxs)("p",{children:["Daniel Meyer - RxDB",(0,l.jsx)("br",{}),"Friedrichstra\xdfe 13",(0,l.jsx)("br",{}),"70174 Stuttgart",(0,l.jsx)("br",{}),"Email: ",(0,l.jsx)("br",{}),(0,l.jsx)("img",{src:"/files/imprint-email.png",alt:"RxDB Email"})]})]}),(0,l.jsxs)("div",{className:"redirectBox",style:{padding:"10%"},children:[(0,l.jsx)("h3",{children:"German Legal Notice"}),(0,l.jsx)("h6",{children:"Umsatzsteuer-ID nach \xa727a Umsatzsteuergesetz"}),"DE357840955",(0,l.jsx)("h6",{children:"Verantwortlich f\xfcr den Inhalt (gem. \xa7 55 Abs. 2 RStV)"}),"Der oben genannte Eigent\xfcmer.",(0,l.jsx)("h6",{children:"Hinweis gem\xe4\xdf Online-Streitbeilegungs-Verordnung"}),(0,l.jsx)("p",{children:"Nach geltendem Recht sind wir verpflichtet, Verbraucher auf die Existenz der Europ\xe4ischen Online-Streitbeilegungs-Plattform hinzuweisen, welche f\xfcr die Beilegung von Streitigkeiten genutzt werden kann, ohne dass ein Gericht eingeschaltet werden muss. F\xfcr die Einrichtung der Plattform ist die Europ\xe4ische Kommission zust\xe4ndig. Die Europ\xe4ische Online-Streitbeilegungs-Plattform ist hier zu finden: http://ec.europa.eu/odr. Wir weisen ausdr\xfccklich darauf hin, dass wir nicht bereit sind, uns am Streitbeilegungsverfahren im Rahmen der Europ\xe4ischen Online-Streitbeilegungs-Plattform zu beteiligen."}),(0,l.jsx)("h6",{children:"\xa7 1 Warnhinweis zu Inhalten"}),(0,l.jsx)("p",{children:"Alle Inhalte dieser Webseite wurden nach Treu und Glauben mit gr\xf6\xdftm\xf6glicher Sorgfalt erstellt. Wir \xfcbernehmen keine Gew\xe4hr f\xfcr die Richtigkeit und Aktualit\xe4t der bereitgestellten Inhalte und garantieren nicht, dass diese Daten jederzeit auf dem aktuellen Stand sind. Diese Webseite kann technische Ungenauigkeiten oder typographische Fehler enthalten. Wir behalten uns vor, die Informationen dieser Webseite jederzeit und ohne vorherige Ank\xfcndigung zu \xe4ndern, zu aktualisieren oder zu l\xf6schen. In keinem Fall haften wir Ihnen oder Dritten gegen\xfcber f\xfcr irgendwelche direkten, indirekten, speziellen oder sonstigen Sch\xe4den jeglicher Art. "}),(0,l.jsx)("h6",{children:"\xa7 2 Externe Links / Externe Verkn\xfcpfungen"}),(0,l.jsx)("p",{children:"Diese Webseite enth\xe4lt Verkn\xfcpfungen zu Webseiten Dritter, zum Beispiel eingebunden durch Links oder Buttons. Diese Webseiten unterliegen der Haftung der jeweiligen Betreiber. Bei der erstmaligen Verkn\xfcpfung jeglicher Webseiten Dritter haben wir die fremden Inhalte auf das Bestehen etwaiger Rechtsverst\xf6\xdfe \xfcberpr\xfcft. Zum Zeitpunkt der \xdcberpr\xfcfung waren keine Rechtsverst\xf6\xdfe ersichtlich. Wir haben keinerlei Einfluss auf die aktuelle und zuk\xfcnftige Gestaltung und auf die Inhalte der verkn\xfcpften Seiten. Das Setzen von externen Verkn\xfcpfungen bedeutet nicht, dass wir uns die hinter der Verkn\xfcpfung liegenden Inhalte zu eigen machen. Eine st\xe4ndige Kontrolle der Inhalte s\xe4mtlicher externen Verkn\xfcpfungen ist ohne konkrete Hinweise auf Rechtsverst\xf6\xdfe nicht zumutbar. Bei Kenntnisnahme von Rechtsverst\xf6\xdfen werden betroffene Inhalte oder Verkn\xfcpfungen unverz\xfcglich gel\xf6scht."}),(0,l.jsx)("h6",{children:"\xa7 3 Urheber- und Leistungsschutzrechte"}),(0,l.jsx)("p",{children:"Die auf dieser Webseite ver\xf6ffentlichten Inhalte unterliegen dem deutschen Urheber- und Leistungsschutzrecht. Jede vom deutschen Urheber- und Leistungsschutzrecht nicht zugelassene Verwertung bedarf der vorherigen schriftlichen Zustimmung unsererseits oder des jeweiligen Rechteinhabers. Dies gilt insbesondere f\xfcr jede Art oder Abwandlung der Vervielf\xe4ltigung, Bearbeitung, Verarbeitung, \xdcbersetzung, Einspeicherung und Wiedergabe von Inhalten in jeglicher Form. Die unerlaubte Vervielf\xe4ltigung oder Weitergabe einzelner Inhalte oder kompletter Seiten ist nicht gestattet und strafbar. Die Darstellung dieser Webseite in fremden Frames ist nur mit schriftlicher Erlaubnis unsererseits zul\xe4ssig."}),(0,l.jsx)("h6",{children:"\xa7 4 Besondere Nutzungsbedingungen"}),(0,l.jsx)("p",{children:"Soweit besondere Bedingungen f\xfcr einzelne Nutzungen dieser Webseite von den vorgenannten Paragraphen abweichen, wird an entsprechender Stelle ausdr\xfccklich darauf hingewiesen. In diesem Falle gelten im jeweiligen Einzelfall die besonderen Nutzungsbedingungen. "}),(0,l.jsx)("h6",{children:"\xa7 5 Rechtswirksamkeit dieses Haftungsausschlusses"}),(0,l.jsx)("p",{children:"Dieser Haftungsausschluss ist als Teil des Internetangebotes zu betrachten, von welchem aus auf diese Seite verwiesen wurde. Sofern Teile oder einzelne Formulierungen dieses Textes der geltenden Rechtslage nicht, nicht mehr oder nicht vollst\xe4ndig entsprechen sollten, bleiben die \xfcbrigen Teile des Dokumentes in ihrem Inhalt und ihrer G\xfcltigkeit davon unber\xfchrt."})]})]})})]})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/51014a8a.c98a88e4.js b/docs/assets/js/51014a8a.c98a88e4.js
deleted file mode 100644
index 40d9ffe7419..00000000000
--- a/docs/assets/js/51014a8a.c98a88e4.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9460],{8453(e,n,s){s.d(n,{R:()=>r,x:()=>a});var i=s(6540);const t={},o=i.createContext(t);function r(e){const n=i.useContext(o);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),i.createElement(o.Provider,{value:n},e.children)}},9777(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>r,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"articles/websockets-sse-polling-webrtc-webtransport","title":"WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport","description":"Learn the unique benefits and pitfalls of each real-time tech. Make informed decisions on WebSockets, SSE, Polling, WebRTC, and WebTransport.","source":"@site/docs/articles/websockets-sse-polling-webrtc-webtransport.md","sourceDirName":"articles","slug":"/articles/websockets-sse-polling-webrtc-webtransport.html","permalink":"/articles/websockets-sse-polling-webrtc-webtransport.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport","slug":"websockets-sse-polling-webrtc-webtransport.html","description":"Learn the unique benefits and pitfalls of each real-time tech. Make informed decisions on WebSockets, SSE, Polling, WebRTC, and WebTransport."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB - The JSON Database Built for JavaScript","permalink":"/articles/json-database.html"},"next":{"title":"Using localStorage in Modern Applications - A Comprehensive Guide","permalink":"/articles/localstorage.html"}}');var t=s(4848),o=s(8453);const r={title:"WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport",slug:"websockets-sse-polling-webrtc-webtransport.html",description:"Learn the unique benefits and pitfalls of each real-time tech. Make informed decisions on WebSockets, SSE, Polling, WebRTC, and WebTransport."},a="WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport",l={},c=[{value:"What is Long Polling?",id:"what-is-long-polling",level:3},{value:"What are WebSockets?",id:"what-are-websockets",level:3},{value:"What are Server-Sent-Events?",id:"what-are-server-sent-events",level:3},{value:"What is the WebTransport API?",id:"what-is-the-webtransport-api",level:3},{value:"What is WebRTC?",id:"what-is-webrtc",level:3},{value:"Limitations of the technologies",id:"limitations-of-the-technologies",level:2},{value:"Sending Data in both directions",id:"sending-data-in-both-directions",level:3},{value:"6-Requests per Domain Limit",id:"6-requests-per-domain-limit",level:3},{value:"Connections are not kept open on mobile apps",id:"connections-are-not-kept-open-on-mobile-apps",level:3},{value:"Proxies and Firewalls",id:"proxies-and-firewalls",level:3},{value:"Performance Comparison",id:"performance-comparison",level:2},{value:"Latency",id:"latency",level:3},{value:"Throughput",id:"throughput",level:3},{value:"Scalability and Server Load",id:"scalability-and-server-load",level:3},{value:"Recommendations and Use-Case Suitability",id:"recommendations-and-use-case-suitability",level:2},{value:"Known Problems",id:"known-problems",level:2},{value:"A client can miss out events when reconnecting",id:"a-client-can-miss-out-events-when-reconnecting",level:3},{value:"Company firewalls can cause problems",id:"company-firewalls-can-cause-problems",level:3},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.header,{children:(0,t.jsx)(n.h1,{id:"websockets-vs-server-sent-events-vs-long-polling-vs-webrtc-vs-webtransport",children:"WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport"})}),"\n",(0,t.jsxs)(n.p,{children:["For modern real-time web applications, the ability to send events from the server to the client is indispensable. This necessity has led to the development of several methods over the years, each with its own set of advantages and drawbacks. Initially, ",(0,t.jsx)(n.a,{href:"#what-is-long-polling",children:"long-polling"})," was the only option available. It was then succeeded by ",(0,t.jsx)(n.a,{href:"#what-are-websockets",children:"WebSockets"}),", which offered a more robust solution for bidirectional communication. Following WebSockets, ",(0,t.jsx)(n.a,{href:"#what-are-server-sent-events",children:"Server-Sent Events (SSE)"})," provided a simpler method for one-way communication from server to client. Looking ahead, the ",(0,t.jsx)(n.a,{href:"#what-is-the-webtransport-api",children:"WebTransport"})," protocol promises to revolutionize this landscape even further by providing a more efficient, flexible, and scalable approach. For niche use cases, ",(0,t.jsx)(n.a,{href:"#what-is-webrtc",children:"WebRTC"})," might also be considered for server-client events."]}),"\n",(0,t.jsxs)(n.p,{children:["This article aims to delve into these technologies, comparing their performance, highlighting their benefits and limitations, and offering recommendations for various use cases to help developers make informed decisions when building real-time web applications. It is a condensed summary of my gathered experience when I implemented the ",(0,t.jsx)(n.a,{href:"/replication.html",children:"RxDB Sync Engine"})," to be compatible with various backend technologies."]}),"\n",(0,t.jsx)("center",{children:(0,t.jsx)("a",{href:"https://rxdb.info/",children:(0,t.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,t.jsx)(n.h3,{id:"what-is-long-polling",children:"What is Long Polling?"}),"\n",(0,t.jsx)(n.p,{children:'Long polling was the first "hack" to enable a server-client messaging method that can be used in browsers over HTTP. The technique emulates server push communications with normal XHR requests. Unlike traditional polling, where the client repeatedly requests data from the server at regular intervals, long polling establishes a connection to the server that remains open until new data is available. Once the server has new information, it sends the response to the client, and the connection is closed. Immediately after receiving the server\'s response, the client initiates a new request, and the process repeats. This method allows for more immediate data updates and reduces unnecessary network traffic and server load. However, it can still introduce delays in communication and is less efficient than other real-time technologies like WebSockets.'}),"\n",(0,t.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,t.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,t.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// long-polling in a JavaScript client"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" longPoll"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'http://example.com/poll'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .then"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(response "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"())"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .then"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(data "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Received data:"'}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" data);"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" longPoll"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Immediately establish a new long polling request"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .catch"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(error "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Errors can appear in normal conditions when a "})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * connection timeout is reached or when the client goes offline."})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * On errors we just restart the polling after some delay."})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" setTimeout"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(longPoll"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10000"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"longPoll"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Initiate the long polling"})]})]})})}),"\n",(0,t.jsx)(n.p,{children:"Implementing long-polling on the client side is pretty simple, as shown in the code above. However on the backend there can be multiple difficulties to ensure the client receives all events and does not miss out updates when the client is currently reconnecting."}),"\n",(0,t.jsx)(n.h3,{id:"what-are-websockets",children:"What are WebSockets?"}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WebSocket?retiredLocale=de",children:"WebSockets"})," provide a full-duplex communication channel over a single, long-lived connection between the client and server. This technology enables browsers and servers to exchange data without the overhead of HTTP request-response cycles, facilitating real-time data transfer for applications like live chat, gaming, or financial trading platforms. WebSockets represent a significant advancement over traditional HTTP by allowing both parties to send data independently once the connection is established, making it ideal for scenarios that require low latency and high-frequency updates."]}),"\n",(0,t.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,t.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,t.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// WebSocket in a JavaScript client"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" WebSocket"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'ws://example.com'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"socket"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onopen"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(event) {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Connection established'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Sending a message to the server"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".send"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Hello Server!'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"socket"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onmessage"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(event) {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Message from server:'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" event"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".data);"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,t.jsxs)(n.p,{children:["While the basics of the WebSocket API are easy to use it has shown to be rather complex in production. A socket can loose connection and must be re-created accordingly. Especially detecting if a connection is still usable or not, can be very tricky. Mostly you would add a ",(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#pings_and_pongs_the_heartbeat_of_websockets",children:"ping-and-pong"})," heartbeat to ensure that the open connection is not closed.\nThis complexity is why most people use a library on top of WebSockets like ",(0,t.jsx)(n.a,{href:"https://socket.io/",children:"Socket.IO"})," which handles all these cases and even provides fallbacks to long-polling if required."]}),"\n",(0,t.jsx)(n.h3,{id:"what-are-server-sent-events",children:"What are Server-Sent-Events?"}),"\n",(0,t.jsx)(n.p,{children:"Server-Sent Events (SSE) provide a standard way to push server updates to the client over HTTP. Unlike WebSockets, SSEs are designed exclusively for one-way communication from server to client, making them ideal for scenarios like live news feeds, sports scores, or any situation where the client needs to be updated in real time without sending data to the server."}),"\n",(0,t.jsx)(n.p,{children:"You can think of Server-Sent-Events as a single HTTP request where the backend does not send the whole body at once, but instead keeps the connection open and trickles the answer by sending a single line each time an event has to be send to the client."}),"\n",(0,t.jsxs)(n.p,{children:["Creating a connection for receiving events with SSE is straightforward. On the client side in a browser, you initialize an ",(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/EventSource",children:"EventSource"})," instance with the URL of the server-side script that generates the events."]}),"\n",(0,t.jsx)(n.p,{children:"Listening for messages involves attaching event handlers directly to the EventSource instance. The API distinguishes between generic message events and named events, allowing for more structured communication. Here's how you can set it up in JavaScript:"}),"\n",(0,t.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,t.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,t.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Connecting to the server-side event stream"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" evtSource"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" EventSource"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"https://example.com/events"'}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Handling generic message events"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"evtSource"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onmessage"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" event "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'got message: '"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" event"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".data);"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,t.jsx)(n.p,{children:"In difference to WebSockets, an EventSource will automatically reconnect on connection loss."}),"\n",(0,t.jsxs)(n.p,{children:["On the server side, your script must set the ",(0,t.jsx)(n.code,{children:"Content-Type"})," header to ",(0,t.jsx)(n.code,{children:"text/event-stream"})," and format each message according to the ",(0,t.jsx)(n.a,{href:"https://www.w3.org/TR/2012/WD-eventsource-20120426/",children:"SSE specification"}),". This includes specifying event types, data payloads, and optional fields like event ID and retry timing."]}),"\n",(0,t.jsx)(n.p,{children:"Here's how you can set up a simple SSE endpoint in a Node.js Express app:"}),"\n",(0,t.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,t.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,t.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" express "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'express'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" app"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" express"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" PORT"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" process"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"env"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"PORT"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ||"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 3000"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'/events'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (req"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" res) "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".writeHead"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"200"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Content-Type'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/event-stream'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Cache-Control'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'no-cache'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Connection'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'keep-alive'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" sendEvent"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (data) "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // all message lines must be prefixed with 'data: '"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" formattedData"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `data: "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(data)"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"\\n\\n`"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".write"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(formattedData);"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Send an event every 2 seconds"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" intervalId"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" setInterval"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" message"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" time"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".toTimeString"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" message"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Hello from the server!'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" sendEvent"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(message);"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 2000"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Clean up when the connection is closed"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" req"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".on"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'close'"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" clearInterval"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(intervalId);"})]}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".end"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,t.jsx)(n.span,{"data-line":"",children:(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,t.jsxs)(n.span,{"data-line":"",children:[(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".listen"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"PORT"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`Server running on http://localhost:"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"PORT"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"}),(0,t.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]})]})})}),"\n",(0,t.jsx)(n.h3,{id:"what-is-the-webtransport-api",children:"What is the WebTransport API?"}),"\n",(0,t.jsxs)(n.p,{children:["WebTransport is a cutting-edge API designed for efficient, low-latency communication between web clients and servers. It leverages the ",(0,t.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/HTTP/3",children:"HTTP/3 QUIC protocol"})," to enable a variety of data transfer capabilities, such as sending data over multiple streams, in both reliable and unreliable manners, and even allowing data to be sent out of order. This makes WebTransport a powerful tool for applications requiring high-performance networking, such as real-time gaming, live streaming, and collaborative platforms. However, it's important to note that WebTransport is currently a working draft and has not yet achieved widespread adoption.\nAs of now (March 2024), WebTransport is in a ",(0,t.jsx)(n.a,{href:"https://w3c.github.io/webtransport/",children:"Working Draft"})," and not widely supported. You cannot yet use WebTransport in the ",(0,t.jsx)(n.a,{href:"https://caniuse.com/webtransport",children:"Safari browser"})," and there is also no native support ",(0,t.jsx)(n.a,{href:"https://github.com/w3c/webtransport/issues/511",children:"in Node.js"}),". This limits its usability across different platforms and environments."]}),"\n",(0,t.jsx)(n.p,{children:"Even when WebTransport will become widely supported, its API is very complex to use and likely it would be something where people build libraries on top of WebTransport, not using it directly in an application's sourcecode."}),"\n",(0,t.jsx)(n.h3,{id:"what-is-webrtc",children:"What is WebRTC?"}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.a,{href:"https://webrtc.org/",children:"WebRTC"})," (Web Real-Time Communication) is an open-source project and API standard that enables real-time communication (RTC) capabilities directly within web browsers and mobile applications without the need for complex server infrastructure or the installation of additional plugins. It supports peer-to-peer connections for streaming audio, video, and data exchange between browsers. ",(0,t.jsx)(n.a,{href:"/replication-webrtc.html",children:"WebRTC"})," is designed to work through NATs and firewalls, utilizing protocols like ICE, STUN, and TURN to establish a connection between peers."]}),"\n",(0,t.jsx)(n.p,{children:"While WebRTC is made to be used for client-client interactions, it could also be leveraged for server-client communication where the server just simulated being also a client. This approach only makes sense for niche use cases which is why in the following WebRTC will be ignored as an option."}),"\n",(0,t.jsx)(n.p,{children:"The problem is that for WebRTC to work, you need a signaling-server anyway which would then again run over websockets, SSE or WebTransport. This defeats the purpose of using WebRTC as a replacement for these technologies."}),"\n",(0,t.jsx)(n.h2,{id:"limitations-of-the-technologies",children:"Limitations of the technologies"}),"\n",(0,t.jsx)(n.h3,{id:"sending-data-in-both-directions",children:"Sending Data in both directions"}),"\n",(0,t.jsx)(n.p,{children:"Only WebSockets and WebTransport allow to send data in both directions so that you can receive server-data and send client-data over the same connection."}),"\n",(0,t.jsxs)(n.p,{children:["While it would also be possible with ",(0,t.jsx)(n.strong,{children:"Long-Polling"}),' in theory, it is not recommended because sending "new" data to an existing long-polling connection would require\nto do an additional http-request anyway. So instead of doing that you can send data directly from the client to the server with an additional http-request without interrupting\nthe long-polling connection.']}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Server-Sent-Events"})," do not support sending any additional data to the server. You can only do the initial request, and even there you cannot send POST-like data in the http-body by default with the native ",(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/EventSource",children:"EventSource API"}),". Instead you have to put all data inside of the url parameters which is considered a bad practice for security because credentials might leak into server logs, proxies and caches. To fix this problem, ",(0,t.jsx)(n.a,{href:"https://rxdb.info/",children:"RxDB"})," for example uses the ",(0,t.jsx)(n.a,{href:"https://github.com/EventSource/eventsource",children:"eventsource polyfill"})," instead of the native ",(0,t.jsx)(n.code,{children:"EventSource API"}),". This library adds additional functionality like sending ",(0,t.jsx)(n.strong,{children:"custom http headers"}),". Also there is ",(0,t.jsx)(n.a,{href:"https://github.com/Azure/fetch-event-source",children:"this library"})," from microsoft which allows to send body data and use ",(0,t.jsx)(n.code,{children:"POST"})," requests instead of ",(0,t.jsx)(n.code,{children:"GET"}),"."]}),"\n",(0,t.jsx)(n.h3,{id:"6-requests-per-domain-limit",children:"6-Requests per Domain Limit"}),"\n",(0,t.jsx)(n.p,{children:"Most modern browsers allow six connections per domain () which limits the usability of all steady server-to-client messaging methods. The limitation of six connections is even shared across browser tabs so when you open the same page in multiple tabs, they would have to shared the six-connection-pool with each other. This limitation is part of the HTTP/1.1-RFC (which even defines a lower number of only two connections)."}),"\n",(0,t.jsxs)(n.blockquote,{children:["\n",(0,t.jsxs)(n.p,{children:["Quote From ",(0,t.jsx)(n.a,{href:"https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4",children:"RFC 2616 - Section 8.1.4"}),': "Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than ',(0,t.jsx)(n.strong,{children:"2 connections"}),' with any server or proxy. A proxy SHOULD use up to 2*N connections to another server or proxy, where N is the number of simultaneously active users. These guidelines are intended to improve HTTP response times and avoid congestion."']}),"\n"]}),"\n",(0,t.jsxs)(n.p,{children:["While that policy makes sense to prevent website owners from using their visitors to D-DOS other websites, it can be a big problem when multiple connections are required to handle server-client communication for legitimate use cases. To workaround the limitation you have to use HTTP/2 or HTTP/3 with which the browser will only open a single connection per domain and then use multiplexing to run all data through a single connection. While this gives you a virtually infinity amount of parallel connections, there is a ",(0,t.jsx)(n.a,{href:"https://www.rfc-editor.org/rfc/rfc7540#section-6.5.2",children:"SETTINGS_MAX_CONCURRENT_STREAMS"})," setting which limits the actually connections amount. The default is 100 concurrent streams for most configurations."]}),"\n",(0,t.jsxs)(n.p,{children:['In theory the connection limit could also be increased by the browser, at least for specific APIs like EventSource, but the issues have been marked as "won\'t fix" by ',(0,t.jsx)(n.a,{href:"https://issues.chromium.org/issues/40329530",children:"chromium"})," and ",(0,t.jsx)(n.a,{href:"https://bugzilla.mozilla.org/show_bug.cgi?id=906896",children:"firefox"}),"."]}),"\n",(0,t.jsx)(n.admonition,{title:"Lower the amount of connections in Browser Apps",type:"note",children:(0,t.jsxs)(n.p,{children:["When you build a browser application, you have to assume that your users will use the app not only once, but in multiple browser tabs in parallel.\nBy default you likely will open one server-stream-connection per tab which is often not necessary at all. Instead you open only a single connection and shared it between tabs, no matter how many tabs are open. ",(0,t.jsx)(n.a,{href:"https://rxdb.info/",children:"RxDB"})," does that with the ",(0,t.jsx)(n.a,{href:"/leader-election.html",children:"LeaderElection"})," from the ",(0,t.jsx)(n.a,{href:"https://github.com/pubkey/broadcast-channel",children:"broadcast-channel npm package"})," to only have one stream of replication between server and clients. You can use that package standalone (without RxDB) for any type of application."]})}),"\n",(0,t.jsx)(n.h3,{id:"connections-are-not-kept-open-on-mobile-apps",children:"Connections are not kept open on mobile apps"}),"\n",(0,t.jsxs)(n.p,{children:["In the context of mobile applications running on operating systems like Android and iOS, maintaining open connections, such as those used for WebSockets and the others, poses a significant challenge. Mobile operating systems are designed to automatically move applications into the background after a certain period of inactivity, effectively closing any open connections. This behavior is a part of the operating system's resource management strategy to conserve battery and optimize performance. As a result, developers often rely on ",(0,t.jsx)(n.strong,{children:"mobile push notifications"})," as an efficient and reliable method to send data from servers to clients. Push notifications allow servers to alert the application of new data, prompting an action or update, without the need for a persistent open connection."]}),"\n",(0,t.jsx)(n.h3,{id:"proxies-and-firewalls",children:"Proxies and Firewalls"}),"\n",(0,t.jsxs)(n.p,{children:["From consulting many ",(0,t.jsx)(n.a,{href:"https://rxdb.info/",children:"RxDB"}),' users, it was shown that in enterprise environments (aka "at work") it is often hard to implement a WebSocket server into the infrastructure because many proxies and firewalls block non-HTTP connections. Therefore using the Server-Sent-Events provides and easier way of enterprise integration. Also long-polling uses only plain HTTP-requests and might be an option.']}),"\n",(0,t.jsx)("center",{children:(0,t.jsx)("a",{href:"https://rxdb.info/",children:(0,t.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,t.jsx)(n.h2,{id:"performance-comparison",children:"Performance Comparison"}),"\n",(0,t.jsx)(n.p,{children:"Comparing the performance of WebSockets, Server-Sent Events (SSE), Long-Polling and WebTransport directly involves evaluating key aspects such as latency, throughput, server load, and scalability under various conditions."}),"\n",(0,t.jsxs)(n.p,{children:["First lets look at the raw numbers. A good performance comparison can be found in ",(0,t.jsx)(n.a,{href:"https://github.com/Sh3b0/realtime-web?tab=readme-ov-file#demos",children:"this repo"})," which tests the messages times in a ",(0,t.jsx)(n.a,{href:"https://go.dev/",children:"Go Lang"})," server implementation. Here we can see that the performance of WebSockets, WebRTC and WebTransport are comparable:"]}),"\n",(0,t.jsx)("center",{children:(0,t.jsx)("a",{href:"https://github.com/Sh3b0/realtime-web?tab=readme-ov-file#demos",target:"_blank",children:(0,t.jsx)("img",{src:"../files/websocket-webrtc-webtransport-performance.png",alt:"WebSocket WebRTC WebTransport Performance",width:"360"})})}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsx)(n.p,{children:"Remember that WebTransport is a pretty new technology based on the also new HTTP/3 protocol. In the future (after March 2024) there might be more performance optimizations. Also WebTransport is optimized to use less power which metric is not tested."})}),"\n",(0,t.jsx)(n.p,{children:"Lets also compare the Latency, the throughput and the scalability:"}),"\n",(0,t.jsx)(n.h3,{id:"latency",children:"Latency"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"WebSockets"}),": Offers the lowest latency due to its full-duplex communication over a single, persistent connection. Ideal for real-time applications where immediate data exchange is critical."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Server-Sent Events"}),": Also provides low latency for server-to-client communication but cannot natively send messages back to the server without additional HTTP requests."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Long-Polling"}),": Incurs higher latency as it relies on establishing new HTTP connections for each data transmission, making it less efficient for real-time updates. Also it can occur that the server wants to send an event when the client is still in the process of opening a new connection. In these cases the latency would be significantly larger."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"WebTransport"}),": Promises to offer low latency similar to WebSockets, with the added benefits of leveraging the HTTP/3 protocol for more efficient multiplexing and congestion control."]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"throughput",children:"Throughput"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"WebSockets"}),": Capable of high throughput due to its persistent connection, but throughput can suffer from ",(0,t.jsx)(n.a,{href:"https://chromestatus.com/feature/5189728691290112",children:"backpressure"})," where the client cannot process data as fast as the server is capable of sending it."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Server-Sent Events"}),": Efficient for broadcasting messages to many clients with less overhead than WebSockets, leading to potentially higher throughput for unidirectional server-to-client communication."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Long-Polling"}),": Generally offers lower throughput due to the overhead of frequently opening and closing connections, which consumes more server resources."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"WebTransport"}),": Expected to support high throughput for both unidirectional and bidirectional streams within a single connection, outperforming WebSockets in scenarios requiring multiple streams."]}),"\n"]}),"\n",(0,t.jsx)(n.h3,{id:"scalability-and-server-load",children:"Scalability and Server Load"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"WebSockets"}),": Maintaining a large number of WebSocket connections can significantly increase server load, potentially affecting scalability for applications with many users."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Server-Sent Events"}),': More scalable for scenarios that primarily require updates from server to client, as it uses less connection overhead than WebSockets because it uses "normal" HTTP request without things like ',(0,t.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/HTTP/Protocol_upgrade_mechanism",children:"protocol updates"})," that have to be run with WebSockets."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"Long-Polling"}),": The least scalable due to the high server load generated by frequent connection establishment, making it suitable only as a fallback mechanism."]}),"\n",(0,t.jsxs)(n.li,{children:[(0,t.jsx)(n.strong,{children:"WebTransport"}),": Designed to be highly scalable, benefiting from HTTP/3's efficiency in handling connections and streams, potentially reducing server load compared to WebSockets and SSE."]}),"\n"]}),"\n",(0,t.jsx)(n.h2,{id:"recommendations-and-use-case-suitability",children:"Recommendations and Use-Case Suitability"}),"\n",(0,t.jsxs)(n.p,{children:["In the landscape of server-client communication technologies, each has its distinct advantages and use case suitability. ",(0,t.jsx)(n.strong,{children:"Server-Sent Events"})," (SSE) emerge as the most straightforward option to implement, leveraging the same HTTP/S protocols as traditional web requests, thereby circumventing corporate firewall restrictions and other technical problems that can appear with other protocols. They are easily integrated into Node.js and other server frameworks, making them an ideal choice for applications requiring frequent server-to-client updates, such as news feeds, stock tickers, and live event streaming."]}),"\n",(0,t.jsxs)(n.p,{children:["On the other hand, ",(0,t.jsx)(n.strong,{children:"WebSockets"})," excel in scenarios demanding ongoing, two-way communication. Their ability to support continuous interaction makes them the prime choice for browser games, chat applications, and live sports updates."]}),"\n",(0,t.jsxs)(n.p,{children:["However, ",(0,t.jsx)(n.strong,{children:"WebTransport"}),", despite its potential, faces adoption challenges. It is not widely supported by server frameworks ",(0,t.jsx)(n.a,{href:"https://github.com/w3c/webtransport/issues/511",children:"including Node.js"})," and lacks compatibility with ",(0,t.jsx)(n.a,{href:"https://caniuse.com/webtransport",children:"safari"}),". Moreover, its reliance on HTTP/3 further limits its immediate applicability because many WebServers like nginx only have ",(0,t.jsx)(n.a,{href:"https://nginx.org/en/docs/quic.html",children:"experimental"})," HTTP/3 support. While promising for future applications with its support for both reliable and unreliable data transmission, WebTransport is not yet a viable option for most use cases."]}),"\n",(0,t.jsxs)(n.p,{children:[(0,t.jsx)(n.strong,{children:"Long-Polling"}),", once a common technique, is now largely outdated due to its inefficiency and the high overhead of repeatedly establishing new HTTP connections. Although it may serve as a fallback in environments lacking support for WebSockets or SSE, its use is generally discouraged due to significant performance limitations."]}),"\n",(0,t.jsx)(n.h2,{id:"known-problems",children:"Known Problems"}),"\n",(0,t.jsx)(n.p,{children:"For all of the realtime streaming technologies, there are known problems. When you build anything on top of them, keep these in mind."}),"\n",(0,t.jsx)(n.h3,{id:"a-client-can-miss-out-events-when-reconnecting",children:"A client can miss out events when reconnecting"}),"\n",(0,t.jsx)(n.p,{children:"When a client is connecting, reconnecting or offline, it can miss out events that happened on the server but could not be streamed to the client.\nThis missed out events are not relevant when the server is streaming the full content each time anyway, like on a live updating stock ticker.\nBut when the backend is made to stream partial results, you have to account for missed out events. Fixing that on the backend scales pretty bad because the backend would have to remember for each client which events have been successfully send already. Instead this should be implemented with client side logic."}),"\n",(0,t.jsxs)(n.p,{children:["The ",(0,t.jsx)(n.a,{href:"/replication.html",children:"RxDB Sync Engine"})," for example uses two modes of operation for that. One is the ",(0,t.jsx)(n.a,{href:"/replication.html#checkpoint-iteration",children:"checkpoint iteration mode"})," where normal http requests are used to iterate over backend data, until the client is in sync again. Then it can switch to ",(0,t.jsx)(n.a,{href:"/replication.html#event-observation",children:"event observation mode"})," where updates from the realtime-stream are used to keep the client in sync. Whenever a client disconnects or has any error, the replication shortly switches to ",(0,t.jsx)(n.a,{href:"/replication.html#checkpoint-iteration",children:"checkpoint iteration mode"})," until the client is in sync again. This method accounts for missed out events and ensures that clients can always sync to the exact equal state of the server."]}),"\n",(0,t.jsx)(n.h3,{id:"company-firewalls-can-cause-problems",children:"Company firewalls can cause problems"}),"\n",(0,t.jsx)(n.p,{children:"There are many known problems with company infrastructure when using any of the streaming technologies. Proxies and firewall can block traffic or unintentionally break requests and responses. Whenever you implement a realtime app in such an infrastructure, make sure you first test out if the technology itself works for you."}),"\n",(0,t.jsx)(n.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,t.jsxs)(n.ul,{children:["\n",(0,t.jsxs)(n.li,{children:["Check out the ",(0,t.jsx)(n.a,{href:"https://news.ycombinator.com/item?id=39745993",children:"hackernews discussion of this article"})]}),"\n",(0,t.jsxs)(n.li,{children:["Shared/Like my ",(0,t.jsx)(n.a,{href:"https://twitter.com/rxdbjs/status/1769507055298064818",children:"announcement tweet"})]}),"\n",(0,t.jsxs)(n.li,{children:["Learn how to use Server-Sent-Events to ",(0,t.jsx)(n.a,{href:"/replication-http.html#pullstream-for-ongoing-changes",children:"replicate a client side RxDB database with your backend"}),"."]}),"\n",(0,t.jsxs)(n.li,{children:["Learn how to use RxDB with the ",(0,t.jsx)(n.a,{href:"/quickstart.html",children:"RxDB Quickstart"})]}),"\n",(0,t.jsxs)(n.li,{children:["Check out the ",(0,t.jsx)(n.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB github repo"})," and leave a star \u2b50"]}),"\n"]})]})}function d(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(h,{...e})}):h(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/51038524.bb23a7bd.js b/docs/assets/js/51038524.bb23a7bd.js
deleted file mode 100644
index 2eb7611f7b0..00000000000
--- a/docs/assets/js/51038524.bb23a7bd.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4889],{952(e,r,s){s.r(r),s.d(r,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>i,metadata:()=>n,toc:()=>d});const n=JSON.parse('{"id":"rx-storage-memory-mapped","title":"Blazing-Fast Memory Mapped RxStorage","description":"Boost your app\'s performance with Memory Mapped RxStorage. Query and write in-memory while seamlessly persisting data to your chosen storage.","source":"@site/docs/rx-storage-memory-mapped.md","sourceDirName":".","slug":"/rx-storage-memory-mapped.html","permalink":"/rx-storage-memory-mapped.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Blazing-Fast Memory Mapped RxStorage","slug":"rx-storage-memory-mapped.html","description":"Boost your app\'s performance with Memory Mapped RxStorage. Query and write in-memory while seamlessly persisting data to your chosen storage."},"sidebar":"tutorialSidebar","previous":{"title":"SharedWorker RxStorage \ud83d\udc51","permalink":"/rx-storage-shared-worker.html"},"next":{"title":"Sharding \ud83d\udc51","permalink":"/rx-storage-sharding.html"}}');var a=s(4848),o=s(8453);const i={title:"Blazing-Fast Memory Mapped RxStorage",slug:"rx-storage-memory-mapped.html",description:"Boost your app's performance with Memory Mapped RxStorage. Query and write in-memory while seamlessly persisting data to your chosen storage."},t="Memory Mapped RxStorage",l={},d=[{value:"Pros",id:"pros",level:2},{value:"Cons",id:"cons",level:2},{value:"Using the Memory-Mapped RxStorage",id:"using-the-memory-mapped-rxstorage",level:2},{value:"Multi-Tab Support",id:"multi-tab-support",level:2},{value:"Encryption of the persistent data",id:"encryption-of-the-persistent-data",level:2},{value:"Await Write Persistence",id:"await-write-persistence",level:2},{value:"Block Size Limit",id:"block-size-limit",level:2}];function c(e){const r={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(r.header,{children:(0,a.jsx)(r.h1,{id:"memory-mapped-rxstorage",children:"Memory Mapped RxStorage"})}),"\n",(0,a.jsxs)(r.p,{children:["The memory mapped ",(0,a.jsx)(r.a,{href:"/rx-storage.html",children:"RxStorage"})," is a wrapper around any other RxStorage. The wrapper creates an in-memory storage that is used for query and write operations. This memory instance is kept persistent with a given underlying storage."]}),"\n",(0,a.jsx)(r.h2,{id:"pros",children:"Pros"}),"\n",(0,a.jsxs)(r.ul,{children:["\n",(0,a.jsx)(r.li,{children:"Improves read/write performance because these operations run against the in-memory storage."}),"\n",(0,a.jsx)(r.li,{children:"Decreases initial page load because it load all data in a single bulk request. It even detects if the database is used for the first time and then it does not have to await the creation of the persistent storage."}),"\n",(0,a.jsx)(r.li,{children:"Can store encrypted data on disc while still being able to run queries on the non-encrypted in-memory state."}),"\n"]}),"\n",(0,a.jsx)(r.h2,{id:"cons",children:"Cons"}),"\n",(0,a.jsxs)(r.ul,{children:["\n",(0,a.jsx)(r.li,{children:"It does not support attachments because storing big attachments data in-memory should not be done."}),"\n",(0,a.jsxs)(r.li,{children:["When the JavaScript process is killed ungracefully like when the browser crashes or the power of the PC is terminated, it might happen that some memory writes are not persisted to the parent storage. This can be prevented with the ",(0,a.jsx)(r.code,{children:"awaitWritePersistence"})," flag."]}),"\n",(0,a.jsx)(r.li,{children:"The memory-mapped storage can only be used if all data fits into the memory of the JavaScript process. This is normally not a problem because a browser has much memory these days and plain JSON document data is not that big."}),"\n",(0,a.jsxs)(r.li,{children:["Because it has to await an initial data loading from the parent storage into the memory, initial page load time can increase when much data is already stored. This is likely not a problem when you store less than ",(0,a.jsx)(r.code,{children:"10k"})," documents."]}),"\n",(0,a.jsxs)(r.li,{children:["The ",(0,a.jsx)(r.code,{children:"memory-mapped"})," storage is part of ",(0,a.jsx)(r.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"}),". It is not part of the default RxDB core module."]}),"\n"]}),"\n",(0,a.jsx)(r.h2,{id:"using-the-memory-mapped-rxstorage",children:"Using the Memory-Mapped RxStorage"}),"\n",(0,a.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" getMemoryMappedRxStorage"})}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-memory-mapped'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here we use the IndexedDB RxStorage as persistence storage."})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" * Any other RxStorage can also be used."})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" parentStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap the persistent storage with the memory-mapped storage."})}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getMemoryMappedRxStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" parentStorage"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// create the RxDatabase like you would do with any other RxStorage"})}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myDatabase,"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/** ... **/"})})]})})}),"\n",(0,a.jsx)(r.h2,{id:"multi-tab-support",children:"Multi-Tab Support"}),"\n",(0,a.jsxs)(r.p,{children:["By how the memory-mapped storage works, it is not possible to have the same storage open in multiple JavaScript processes. So when you use this in a browser application, you can not open multiple databases when the app is used in multiple browser tabs.\nTo solve this, use the ",(0,a.jsx)(r.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker Plugin"})," so that the memory-mapped storage runs inside of a SharedWorker exactly once and is then reused for all browser tabs."]}),"\n",(0,a.jsx)(r.p,{children:"If you have a single JavaScript process, like in a React Native app, you do not have to care about this and can just use the memory-mapped storage in the main process."}),"\n",(0,a.jsx)(r.h2,{id:"encryption-of-the-persistent-data",children:"Encryption of the persistent data"}),"\n",(0,a.jsxs)(r.p,{children:["Normally RxDB is not capable of running queries on encrypted fields. But when you use the memory-mapped RxStorage, you can store the document data encrypted on disc, while being able to run queries on the not encrypted in-memory state. Make sure you use the encryption storage wrapper around the persistent storage, ",(0,a.jsx)(r.strong,{children:"NOT"})," around the memory-mapped storage as a whole."]}),"\n",(0,a.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" getMemoryMappedRxStorage"})}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-memory-mapped'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedKeyEncryptionWebCryptoStorage } "}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/encryption-web-crypto'"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getMemoryMappedRxStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyEncryptionWebCryptoStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myDatabase,"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/** ... **/"})})]})})}),"\n",(0,a.jsx)(r.h2,{id:"await-write-persistence",children:"Await Write Persistence"}),"\n",(0,a.jsxs)(r.p,{children:["Running operations on the memory-mapped storage by default returns directly when the operation has run on the in-memory state and then persist changes in the background.\nSometimes you might want to ensure write operations is persisted, you can do this by setting ",(0,a.jsx)(r.code,{children:"awaitWritePersistence: true"}),"."]}),"\n",(0,a.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getMemoryMappedRxStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" awaitWritePersistence"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(r.h2,{id:"block-size-limit",children:"Block Size Limit"}),"\n",(0,a.jsxs)(r.p,{children:["During cleanup, the memory-mapped storage will merge many small write-blocks into single big blocks for better initial load performance.\nThe ",(0,a.jsx)(r.code,{children:"blockSizeLimit"})," defines the maximum of how many documents get stored in a single block. The default is ",(0,a.jsx)(r.code,{children:"10000"}),"."]}),"\n",(0,a.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getMemoryMappedRxStorage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" blockSizeLimit"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(r.span,{"data-line":"",children:[(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(r.span,{"data-line":"",children:(0,a.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]})}function h(e={}){const{wrapper:r}={...(0,o.R)(),...e.components};return r?(0,a.jsx)(r,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},8453(e,r,s){s.d(r,{R:()=>i,x:()=>t});var n=s(6540);const a={},o=n.createContext(a);function i(e){const r=n.useContext(o);return n.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function t(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),n.createElement(o.Provider,{value:r},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/51334108.2284fc7c.js b/docs/assets/js/51334108.2284fc7c.js
deleted file mode 100644
index 60cfd3a332a..00000000000
--- a/docs/assets/js/51334108.2284fc7c.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8926],{6996(e,a,i){i.r(a),i.d(a,{assets:()=>l,contentTitle:()=>r,default:()=>p,frontMatter:()=>o,metadata:()=>t,toc:()=>d});const t=JSON.parse('{"id":"articles/mobile-database","title":"Real-Time & Offline - RxDB for Mobile Apps","description":"Explore RxDB as your reliable mobile database. Enjoy offline-first capabilities, real-time sync, and seamless integration for hybrid app development.","source":"@site/docs/articles/mobile-database.md","sourceDirName":"articles","slug":"/articles/mobile-database.html","permalink":"/articles/mobile-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Real-Time & Offline - RxDB for Mobile Apps","slug":"mobile-database.html","description":"Explore RxDB as your reliable mobile database. Enjoy offline-first capabilities, real-time sync, and seamless integration for hybrid app development."},"sidebar":"tutorialSidebar","previous":{"title":"Using localStorage in Modern Applications - A Comprehensive Guide","permalink":"/articles/localstorage.html"},"next":{"title":"RxDB as a Database for Progressive Web Apps (PWA)","permalink":"/articles/progressive-web-app-database.html"}}');var n=i(4848),s=i(8453);const o={title:"Real-Time & Offline - RxDB for Mobile Apps",slug:"mobile-database.html",description:"Explore RxDB as your reliable mobile database. Enjoy offline-first capabilities, real-time sync, and seamless integration for hybrid app development."},r="Mobile Database - RxDB as Database for Mobile Applications",l={},d=[{value:"Understanding Mobile Databases",id:"understanding-mobile-databases",level:2},{value:"Introducing RxDB: A Paradigm Shift in Mobile Database Solutions",id:"introducing-rxdb-a-paradigm-shift-in-mobile-database-solutions",level:2},{value:"Use Cases for RxDB in Hybrid App Development",id:"use-cases-for-rxdb-in-hybrid-app-development",level:2},{value:"Conclusion",id:"conclusion",level:2}];function c(e){const a={a:"a",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",...(0,s.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(a.header,{children:(0,n.jsx)(a.h1,{id:"mobile-database---rxdb-as-database-for-mobile-applications",children:"Mobile Database - RxDB as Database for Mobile Applications"})}),"\n",(0,n.jsxs)(a.p,{children:["In today's digital landscape, mobile applications have become an integral part of our lives. From social media platforms to e-commerce solutions, mobile apps have transformed the way we interact with digital services. At the heart of any mobile app lies the database, a critical component responsible for storing, retrieving, and managing data efficiently. In this article, we will delve into the world of mobile databases, exploring their significance, challenges, and the emergence of ",(0,n.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"})," as a powerful database solution for hybrid app development in frameworks like React Native and Capacitor."]}),"\n",(0,n.jsx)(a.h2,{id:"understanding-mobile-databases",children:"Understanding Mobile Databases"}),"\n",(0,n.jsx)(a.p,{children:"Mobile databases are specialized software systems designed to handle data storage and management for mobile applications. These databases are optimized for the unique requirements of mobile environments, which often include limited device resources, fluctuations in network connectivity, and the need for offline functionality."}),"\n",(0,n.jsxs)(a.p,{children:["There are various types of mobile databases available, each with its own strengths and use cases. Local databases, such as SQLite and Realm, reside directly on the user's device, providing offline capabilities and faster data access. Cloud-based databases, like ",(0,n.jsx)(a.a,{href:"/articles/realtime-database.html",children:"Firebase Realtime Database"})," and Amazon DynamoDB, rely on remote servers to store and retrieve data, enabling synchronization across multiple devices. Hybrid databases, as the name suggests, combine the benefits of both local and cloud-based approaches, offering a balance between offline functionality and data synchronization."]}),"\n",(0,n.jsx)(a.h2,{id:"introducing-rxdb-a-paradigm-shift-in-mobile-database-solutions",children:"Introducing RxDB: A Paradigm Shift in Mobile Database Solutions"}),"\n",(0,n.jsx)("center",{children:(0,n.jsx)("a",{href:"https://rxdb.info/",children:(0,n.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"Mobile Database",width:"220"})})}),"\n",(0,n.jsxs)(a.p,{children:[(0,n.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"}),", also known as Reactive Database, has emerged as a game-changer in the realm of mobile databases. Built on top of popular web technologies like JavaScript, TypeScript, and RxJS (Reactive Extensions for JavaScript), RxDB provides an elegant solution for seamless offline-first capabilities and real-time data synchronization in mobile applications."]}),"\n",(0,n.jsx)(a.p,{children:"Benefits of RxDB for Hybrid App Development"}),"\n",(0,n.jsxs)(a.ol,{children:["\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Offline-First Approach: One of the major advantages of RxDB is its ability to work in an offline mode. It allows mobile applications to store and access data locally, ensuring uninterrupted functionality even when the network connection is weak or unavailable. The database automatically syncs the data with the server once the connection is reestablished, guaranteeing data consistency."}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsxs)(a.p,{children:[(0,n.jsx)(a.a,{href:"/replication.html",children:"Real-Time Data Synchronization"}),": RxDB leverages the power of real-time data synchronization, making it an excellent choice for applications that require collaborative features or live updates. It uses the concept of change streams to detect modifications made to the database and instantly propagates those changes across connected devices. This real-time synchronization enables seamless collaboration and enhances user experience."]}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Reactive Programming Paradigm: RxDB embraces the principles of reactive programming, which simplifies the development process by handling asynchronous events and data streams. By leveraging RxJS observables, developers can write concise, declarative code that reacts to changes in data, ensuring a highly responsive user experience. The reactive programming paradigm enhances code maintainability, scalability, and testability."}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsxs)(a.p,{children:["Easy Integration with Hybrid App Frameworks: RxDB seamlessly integrates with popular hybrid app development frameworks like ",(0,n.jsx)(a.a,{href:"/react-native-database.html",children:"React Native"})," and ",(0,n.jsx)(a.a,{href:"/capacitor-database.html",children:"Capacitor"}),". This compatibility allows developers to leverage the existing ecosystem and tools of these frameworks, making the transition to RxDB smoother and more efficient. By utilizing RxDB within these frameworks, developers can harness the power of a robust database solution without sacrificing the advantages of hybrid app development."]}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Cross-Platform Support: RxDB enables developers to build cross-platform mobile applications that run seamlessly on both iOS and Android devices. This versatility eliminates the need for separate database implementations for different platforms, saving development time and effort. With RxDB, developers can focus on building a unified codebase and delivering a consistent user experience across platforms."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(a.h2,{id:"use-cases-for-rxdb-in-hybrid-app-development",children:"Use Cases for RxDB in Hybrid App Development"}),"\n",(0,n.jsxs)(a.ol,{children:["\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsxs)(a.p,{children:[(0,n.jsx)(a.a,{href:"/offline-first.html",children:"Offline-First Applications"}),": ",(0,n.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"})," is an ideal choice for applications that heavily rely on offline functionality. Whether it's a note-taking app, a task manager, or a survey application, RxDB ensures that users can continue working even when connectivity is compromised. The seamless synchronization capabilities of RxDB ensure that changes made offline are automatically propagated once the device reconnects to the internet."]}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Real-Time Collaboration: Applications that require real-time collaboration, such as messaging platforms or collaborative editing tools, can greatly benefit from RxDB. The real-time synchronization capabilities enable multiple users to work on the same data simultaneously, ensuring that everyone sees the latest updates in real-time."}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Data-Intensive Applications: RxDB's performance and scalability make it suitable for data-intensive applications that handle large datasets or complex data structures. Whether it's a media-rich app, a data visualization tool, or an analytics platform, RxDB can handle the heavy lifting and provide a smooth user experience."}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Cross-Platform Applications: Hybrid app frameworks like React Native and Capacitor have gained popularity due to their ability to build cross-platform applications. By utilizing RxDB within these frameworks, developers can create a unified codebase that runs seamlessly on both iOS and Android, significantly reducing development time and effort."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(a.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,n.jsx)(a.p,{children:"Mobile databases play a vital role in the performance and functionality of mobile applications. RxDB, with its offline-first approach, real-time data synchronization, and seamless integration with hybrid app development frameworks like React Native and Capacitor, offers a robust solution for managing data in mobile apps. By leveraging the power of reactive programming, RxDB empowers developers to build highly responsive, scalable, and cross-platform applications that deliver an exceptional user experience. With its versatility and ease of use, RxDB is undoubtedly a database solution worth considering for hybrid app development. Embrace the power of RxDB and unlock the full potential of your mobile applications."})]})}function p(e={}){const{wrapper:a}={...(0,s.R)(),...e.components};return a?(0,n.jsx)(a,{...e,children:(0,n.jsx)(c,{...e})}):c(e)}},8453(e,a,i){i.d(a,{R:()=>o,x:()=>r});var t=i(6540);const n={},s=t.createContext(n);function o(e){const a=t.useContext(s);return t.useMemo(function(){return"function"==typeof e?e(a):{...a,...e}},[a,e])}function r(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:o(e.components),t.createElement(s.Provider,{value:a},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/5134b15f.c48f7dbd.js b/docs/assets/js/5134b15f.c48f7dbd.js
deleted file mode 100644
index 972d148b295..00000000000
--- a/docs/assets/js/5134b15f.c48f7dbd.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[10],{4066(a,e,t){t.r(e),t.d(e,{default:()=>r});var i=t(544),s=t(4848);function r(){return(0,i.default)({sem:{id:"gads",title:(0,s.jsxs)(s.Fragment,{children:["The easiest way to ",(0,s.jsx)("b",{children:"store"})," and ",(0,s.jsx)("b",{children:"sync"})," Data in Capacitor"]}),appName:"Capacitor",iconUrl:"/files/icons/capacitor.svg",metaTitle:"Capacitor Database"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/55a5b596.49b70407.js b/docs/assets/js/55a5b596.49b70407.js
deleted file mode 100644
index f058e2a2207..00000000000
--- a/docs/assets/js/55a5b596.49b70407.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6717],{4557(s,e,n){n.r(e),n.d(e,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"rx-schema","title":"Design Perfect Schemas in RxDB","description":"Learn how to define, secure, and validate your data in RxDB. Master primary keys, indexes, encryption, and more with the RxSchema approach.","source":"@site/docs/rx-schema.md","sourceDirName":".","slug":"/rx-schema.html","permalink":"/rx-schema.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Design Perfect Schemas in RxDB","slug":"rx-schema.html","description":"Learn how to define, secure, and validate your data in RxDB. Master primary keys, indexes, encryption, and more with the RxSchema approach."},"sidebar":"tutorialSidebar","previous":{"title":"RxDatabase","permalink":"/rx-database.html"},"next":{"title":"RxCollection","permalink":"/rx-collection.html"}}');var i=n(4848),o=n(8453);const a={title:"Design Perfect Schemas in RxDB",slug:"rx-schema.html",description:"Learn how to define, secure, and validate your data in RxDB. Master primary keys, indexes, encryption, and more with the RxSchema approach."},l="RxSchema",t={},c=[{value:"Example",id:"example",level:2},{value:"Create a collection with the schema",id:"create-a-collection-with-the-schema",level:2},{value:"version",id:"version",level:2},{value:"primaryKey",id:"primarykey",level:2},{value:"composite primary key",id:"composite-primary-key",level:3},{value:"Indexes",id:"indexes",level:2},{value:"Index-example",id:"index-example",level:3},{value:"attachments",id:"attachments",level:2},{value:"default",id:"default",level:2},{value:"final",id:"final",level:2},{value:"Non allowed properties",id:"non-allowed-properties",level:2},{value:"FAQ",id:"faq",level:2}];function d(s){const e={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...s.components},{Details:n}=e;return n||function(s,e){throw new Error("Expected "+(e?"component":"object")+" `"+s+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(e.header,{children:(0,i.jsx)(e.h1,{id:"rxschema",children:"RxSchema"})}),"\n",(0,i.jsxs)(e.p,{children:["Schemas define the structure of the documents of a collection. Which field should be used as primary, which fields should be used as indexes and what should be encrypted. Every collection has its own schema. With RxDB, schemas are defined with the ",(0,i.jsx)(e.a,{href:"https://json-schema.org/blog/posts/rxdb-case-study",children:"jsonschema"}),"-standard which you might know from other projects."]}),"\n",(0,i.jsx)(e.h2,{id:"example",children:"Example"}),"\n",(0,i.jsx)(e.p,{children:"In this example-schema we define a hero-collection with the following settings:"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsx)(e.li,{children:"the version-number of the schema is 0"}),"\n",(0,i.jsxs)(e.li,{children:["the name-property is the ",(0,i.jsx)(e.strong,{children:"primaryKey"}),". This means its a unique, indexed, required ",(0,i.jsx)(e.code,{children:"string"})," which can be used to definitely find a single document."]}),"\n",(0,i.jsx)(e.li,{children:"the color-field is required for every document"}),"\n",(0,i.jsx)(e.li,{children:"the healthpoints-field must be a number between 0 and 100"}),"\n",(0,i.jsx)(e.li,{children:"the secret-field stores an encrypted value"}),"\n",(0,i.jsx)(e.li,{children:"the birthyear-field is final which means it is required and cannot be changed"}),"\n",(0,i.jsx)(e.li,{children:"the skills-attribute must be an array with objects which contain the name and the damage-attribute. There is a maximum of 5 skills per hero."}),"\n",(0,i.jsx)(e.li,{children:"Allows adding attachments and store them encrypted"}),"\n"]}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "title"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "hero schema"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "version"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "description"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "describes a simple hero"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "primaryKey"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "name"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "object"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "properties"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "name"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "maxLength"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have set maxLength"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "color"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "healthpoints"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "number"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "minimum"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "maximum"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "secret"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "birthyear"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "number"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "final"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "minimum"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 1900"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "maximum"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 2050"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "skills"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "array"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "maxItems"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "uniqueItems"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "items"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "object"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "properties"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "name"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "damage"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "number"'})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "required"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "name"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "color"'})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ]"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "encrypted"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"secret"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "attachments"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "encrypted"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})})]})})}),"\n",(0,i.jsx)(e.h2,{id:"create-a-collection-with-the-schema",children:"Create a collection with the schema"}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" myHeroSchema"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"myDatabase"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:".name);"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// heroes"})})]})})}),"\n",(0,i.jsx)(e.h2,{id:"version",children:"version"}),"\n",(0,i.jsxs)(e.p,{children:["The ",(0,i.jsx)(e.code,{children:"version"})," field is a number, starting with ",(0,i.jsx)(e.code,{children:"0"}),".\nWhen the version is greater than 0, you have to provide the migrationStrategies to create a collection with this schema."]}),"\n",(0,i.jsx)(e.h2,{id:"primarykey",children:"primaryKey"}),"\n",(0,i.jsxs)(e.p,{children:["The ",(0,i.jsx)(e.code,{children:"primaryKey"})," field contains the fieldname of the property that will be used as primary key for the whole collection.\nThe value of the primary key of the document must be a ",(0,i.jsx)(e.code,{children:"string"}),", unique, final and is required."]}),"\n",(0,i.jsx)(e.h3,{id:"composite-primary-key",children:"composite primary key"}),"\n",(0,i.jsx)(e.p,{children:"You can define a composite primary key which gets composed from multiple properties of the document data."}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" mySchema"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" keyCompression"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // set this to true, to enable the keyCompression"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human schema with composite primary'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // where should the composed string be stored"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" key"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // fields that will be used to create the composed key"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" fields"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'lastName'"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ]"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // separator which is used to concat the fields values."})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" separator"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '|'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have set maxLength"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" "})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'lastName'"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(e.p,{children:"You can then find a document by using the relevant parts to create the composite primaryKey:"}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// inserting with composite primary"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // id, <- do not set the id, it will be filled by RxDB"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:"// find by composite primary"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" id"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"schema"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".getPrimaryOfDocumentData"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDocument"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"(id)"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsx)(e.h2,{id:"indexes",children:"Indexes"}),"\n",(0,i.jsx)(e.p,{children:"RxDB supports secondary indexes which are defined at the schema-level of the collection."}),"\n",(0,i.jsxs)(e.p,{children:["Index is only allowed on field types ",(0,i.jsx)(e.code,{children:"string"}),", ",(0,i.jsx)(e.code,{children:"integer"})," and ",(0,i.jsx)(e.code,{children:"number"}),". Some RxStorages allow to use ",(0,i.jsx)(e.code,{children:"boolean"})," fields as index."]}),"\n",(0,i.jsxs)(e.p,{children:["Depending on the field type, you must have set some meta attributes like ",(0,i.jsx)(e.code,{children:"maxLength"})," or ",(0,i.jsx)(e.code,{children:"minimum"}),". This is required so that RxDB\nis able to know the maximum string representation length of a field, which is needed to craft custom indexes on several ",(0,i.jsx)(e.code,{children:"RxStorage"})," implementations."]}),"\n",(0,i.jsx)(e.admonition,{type:"note",children:(0,i.jsxs)(e.p,{children:["RxDB will always append the ",(0,i.jsx)(e.code,{children:"primaryKey"})," to all indexes to ensure a deterministic sort order of query results. You do not have to add the ",(0,i.jsx)(e.code,{children:"primaryKey"})," to any index."]})}),"\n",(0,i.jsx)(e.h3,{id:"index-example",children:"Index-example"}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" schemaWithIndexes"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human schema with indexes'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" keyCompression"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have set maxLength"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- string-fields that are used as an index, must have set maxLength."})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" active"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'boolean'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" familyName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" balance"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // number fields that are used in an index, must have set minimum, maximum and multipleOf"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" minimum"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" maximum"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100000"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" multipleOf"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0.01"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" creditCards"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'array'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" items"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" cvc"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" } "})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'active'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- boolean fields that are used in an index, must be required. "})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ]"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" indexes"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- this will create a simple index for the `firstName` field"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'active'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- this will create a compound-index for these two fields"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'active'"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(e.h1,{id:"internalindexes",children:"internalIndexes"}),"\n",(0,i.jsxs)(e.p,{children:["When you use RxDB on the server-side, you might want to use internalIndexes to speed up internal queries. ",(0,i.jsx)(e.a,{href:"/rx-server.html#server-only-indexes",children:"Read more"})]}),"\n",(0,i.jsx)(e.h2,{id:"attachments",children:"attachments"}),"\n",(0,i.jsxs)(e.p,{children:["To use attachments in the collection, you have to add the ",(0,i.jsx)(e.code,{children:"attachments"}),"-attribute to the schema. ",(0,i.jsx)(e.a,{href:"/rx-attachment.html",children:"See RxAttachment"}),"."]}),"\n",(0,i.jsx)(e.h2,{id:"default",children:"default"}),"\n",(0,i.jsx)(e.p,{children:"Default values can only be defined for first-level fields.\nWhenever you insert a document unset fields will be filled with default-values."}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" schemaWithDefaultAge"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have set maxLength"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'integer'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" default"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 20"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- default will be used"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(e.h2,{id:"final",children:"final"}),"\n",(0,i.jsxs)(e.p,{children:["By setting a field to ",(0,i.jsx)(e.code,{children:"final"}),", you make sure it cannot be modified later. Final fields are always required.\nFinal fields cannot be observed because they will not change."]}),"\n",(0,i.jsx)(e.p,{children:"Advantages:"}),"\n",(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsx)(e.li,{children:"With final fields you can ensure that no-one accidentally modifies the data."}),"\n",(0,i.jsxs)(e.li,{children:["When you enable the ",(0,i.jsx)(e.code,{children:"eventReduce"})," algorithm, some performance-improvements are done."]}),"\n"]}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" schemaWithFinalAge"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have set maxLength"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'integer'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" final"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(e.h2,{id:"non-allowed-properties",children:"Non allowed properties"}),"\n",(0,i.jsxs)(e.p,{children:["The schema is not only used to validate objects before they are written into the database, but also used to map getters to observe and populate single fieldnames, keycompression and other things. Therefore you can not use every schema which would be valid for the spec of ",(0,i.jsx)(e.a,{href:"http://json-schema.org/",children:"json-schema.org"}),".\nFor example, fieldnames must match the regex ",(0,i.jsx)(e.code,{children:"^[a-zA-Z][[a-zA-Z0-9_]*]?[a-zA-Z0-9]$"})," and ",(0,i.jsx)(e.code,{children:"additionalProperties"})," is always set to ",(0,i.jsx)(e.code,{children:"false"}),". But don't worry, RxDB will instantly throw an error when you pass an invalid schema into it."]}),"\n",(0,i.jsxs)(e.p,{children:["Also the following class properties of ",(0,i.jsx)(e.code,{children:"RxDocument"})," cannot be used as top level fields because they would clash when the RxDocument property is accessed:"]}),"\n",(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"["})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "collection"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "_data"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "_propertyCache"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "isInstanceOfRxDocument"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "primaryPath"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "primary"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "revision"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "deleted$"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "deleted$$"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "deleted"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "getLatest"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "$"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "$$"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "get$"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "get$$"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "populate"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "get"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "toJSON"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "toMutableJSON"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "update"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "incrementalUpdate"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "updateCRDT"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "putAttachment"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "putAttachmentBase64"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "getAttachment"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "allAttachments"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "allAttachments$"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "modify"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "incrementalModify"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "patch"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "incrementalPatch"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "_saveData"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "remove"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "incrementalRemove"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "close"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "deleted"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "synced"'})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"})})]})})}),"\n",(0,i.jsx)(e.h2,{id:"faq",children:"FAQ"}),"\n",(0,i.jsxs)(n,{children:[(0,i.jsx)("summary",{children:"How can I store a Date?"}),(0,i.jsxs)("div",{children:[(0,i.jsxs)(e.p,{children:["With RxDB you can only store plain JSON data inside of a document. You cannot store a JavaScript ",(0,i.jsx)(e.code,{children:"new Date()"})," instance directly. This is for performance reasons and because ",(0,i.jsx)(e.code,{children:"Date()"})," is a mutable thing where changing it at any time might cause strange problem that are hard to debug."]}),(0,i.jsxs)(e.p,{children:["To store a date in RxDB, you have to define a string field with a ",(0,i.jsx)(e.code,{children:"format"})," attribute:"]}),(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:' "format"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "date-time"'})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),(0,i.jsxs)(e.p,{children:["When storing the data you have to first transform your ",(0,i.jsx)(e.code,{children:"Date"})," object into a string ",(0,i.jsx)(e.code,{children:"Date.toISOString()"}),".\nBecause the ",(0,i.jsx)(e.code,{children:"date-time"})," is sortable, you can do whatever query operations on that field and even use it as an index."]})]})]}),"\n",(0,i.jsxs)(n,{children:[(0,i.jsx)("summary",{children:"How to store schemaless data?"}),(0,i.jsxs)("div",{children:[(0,i.jsxs)(e.p,{children:['By design, RxDB requires that every collection has a schema. This means you cannot create a truly "schema-less" collection where top-level fields are unknown at schema creation time. RxDB must know about all fields of a document at the top level to perform validation, index creation, and other internal optimizations.\nHowever, there is a way to store data of arbitrary structure at sub-fields. To do this, define a property with ',(0,i.jsx)(e.code,{children:'type: "object"'})," in your schema. For example:"]}),(0,i.jsx)(e.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(e.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(e.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "version"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:": "}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "primaryKey"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:": "}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"id"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:": "}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"object"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "properties"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:": {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "id"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "maxLength"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "myDynamicData"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "type"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "object"'})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // Here you can store any JSON data"})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-comment)"},children:" // because it's an open object."})}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(e.span,{"data-line":"",children:[(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "required"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:": ["}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"id"'}),(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(e.span,{"data-line":"",children:(0,i.jsx)(e.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})})]})]}),"\n",(0,i.jsxs)(n,{children:[(0,i.jsxs)("summary",{children:["Why does RxDB automatically set ",(0,i.jsx)(e.code,{children:"additionalProperties: false"})," at the top level"]}),(0,i.jsxs)("div",{children:[(0,i.jsxs)(e.p,{children:["RxDB automatically sets ",(0,i.jsx)(e.code,{children:"additionalProperties: false"})," at the top level of a schema to ensure that all top-level fields are known in advance. This design choice offers several benefits:"]}),(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsxs)(e.li,{children:["\n",(0,i.jsx)(e.p,{children:"Prevents collisions with RxDocument class properties:\nRxDB documents have built-in class methods (e.g., .toJSON, .save) at the top level. By forbidding unknown top-level properties, we avoid accidental naming collisions with these built-in methods."}),"\n"]}),"\n",(0,i.jsxs)(e.li,{children:["\n",(0,i.jsxs)(e.p,{children:["Avoids conflicts with user-defined ORM functions:\nDevelopers can add custom ",(0,i.jsx)(e.a,{href:"/orm.html",children:"ORM methods"})," to RxDocuments. If top-level properties were unbounded, a property name could accidentally conflict with a method name, leading to unexpected behavior."]}),"\n"]}),"\n",(0,i.jsxs)(e.li,{children:["\n",(0,i.jsxs)(e.p,{children:["Improves TypeScript typings:\nIf RxDB didn't know about all top-level fields, the document type would effectively become ",(0,i.jsx)(e.code,{children:"any"}),". That means a simple typo like ",(0,i.jsx)(e.code,{children:"myDocument.toJOSN()"})," would only be caught at runtime, not at build time. By disallowing unknown properties, TypeScript can provide strict typing and catch errors sooner."]}),"\n"]}),"\n"]})]})]}),"\n",(0,i.jsxs)(n,{children:[(0,i.jsx)("summary",{children:"Can't change the schema of a collection"}),(0,i.jsxs)("div",{children:[(0,i.jsxs)(e.p,{children:["When you make changes to the schema of a collection, you sometimes can get an error like\n",(0,i.jsx)(e.code,{children:"Error: addCollections(): another instance created this collection with a different schema"}),"."]}),(0,i.jsx)(e.p,{children:"This means you have created a collection before and added document-data to it.\nWhen you now just change the schema, it is likely that the new schema does not match the saved documents inside of the collection.\nThis would cause strange bugs and would be hard to debug, so RxDB check's if your schema has changed and throws an error."}),(0,i.jsxs)(e.p,{children:["To change the schema in ",(0,i.jsx)(e.strong,{children:"production"}),"-mode, do the following steps:"]}),(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsxs)(e.li,{children:["Increase the ",(0,i.jsx)(e.code,{children:"version"})," by 1"]}),"\n",(0,i.jsxs)(e.li,{children:["Add the appropriate ",(0,i.jsx)(e.a,{href:"https://pubkey.github.io/rxdb/migration-schema.html",children:"migrationStrategies"})," so the saved data will be modified to match the new schema"]}),"\n"]}),(0,i.jsxs)(e.p,{children:["In ",(0,i.jsx)(e.strong,{children:"development"}),"-mode, the schema-change can be simplified by ",(0,i.jsx)(e.strong,{children:"one of these"})," strategies:"]}),(0,i.jsxs)(e.ul,{children:["\n",(0,i.jsx)(e.li,{children:"Use the memory-storage so your db resets on restart and your schema is not saved permanently"}),"\n",(0,i.jsxs)(e.li,{children:["Call ",(0,i.jsx)(e.code,{children:"removeRxDatabase('mydatabasename', RxStorage);"})," before creating a new RxDatabase-instance"]}),"\n",(0,i.jsxs)(e.li,{children:["Add a timestamp as suffix to the database-name to create a new one each run like ",(0,i.jsx)(e.code,{children:"name: 'heroesDB' + new Date().getTime()"})]}),"\n"]})]})]})]})}function h(s={}){const{wrapper:e}={...(0,o.R)(),...s.components};return e?(0,i.jsx)(e,{...s,children:(0,i.jsx)(d,{...s})}):d(s)}},8453(s,e,n){n.d(e,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(s){const e=r.useContext(o);return r.useMemo(function(){return"function"==typeof s?s(e):{...e,...s}},[e,s])}function l(s){let e;return e=s.disableParentContext?"function"==typeof s.components?s.components(i):s.components||i:a(s.components),r.createElement(o.Provider,{value:e},s.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/58215328.4a9061e5.js b/docs/assets/js/58215328.4a9061e5.js
deleted file mode 100644
index 4c8caa3419a..00000000000
--- a/docs/assets/js/58215328.4a9061e5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5353],{960(e,a,i){i.r(a),i.d(a,{default:()=>t});var s=i(544),n=i(4848);function t(){return(0,s.default)({sem:{id:"gads",metaTitle:"The local Database for Ionic Apps",appName:"Ionic",title:(0,n.jsxs)(n.Fragment,{children:["The easiest way to ",(0,n.jsx)("b",{children:"store"})," and ",(0,n.jsx)("b",{children:"sync"})," Data in Ionic"]}),iconUrl:"/files/icons/ionic.svg"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/5943.0ded4df5.js b/docs/assets/js/5943.0ded4df5.js
deleted file mode 100644
index 68f6f5a3167..00000000000
--- a/docs/assets/js/5943.0ded4df5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5943],{2153(e,n,t){t.d(n,{A:()=>j});t(6540);var i=t(4164),s=t(1312),a=t(7559),r=t(8774);const l="iconEdit_Z9Sw";var c=t(4848);function o({className:e,...n}){return(0,c.jsx)("svg",{fill:"currentColor",height:"20",width:"20",viewBox:"0 0 40 40",className:(0,i.A)(l,e),"aria-hidden":"true",...n,children:(0,c.jsx)("g",{children:(0,c.jsx)("path",{d:"m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"})})})}function d({editUrl:e}){return(0,c.jsxs)(r.A,{to:e,className:a.G.common.editThisPage,children:[(0,c.jsx)(o,{}),(0,c.jsx)(s.A,{id:"theme.common.editThisPage",description:"The link label to edit the current page",children:"Edit this page"})]})}var u=t(4586);function m(e={}){const{i18n:{currentLocale:n}}=(0,u.A)(),t=function(){const{i18n:{currentLocale:e,localeConfigs:n}}=(0,u.A)();return n[e].calendar}();return new Intl.DateTimeFormat(n,{calendar:t,...e})}function h({lastUpdatedAt:e}){const n=new Date(e),t=m({day:"numeric",month:"short",year:"numeric",timeZone:"UTC"}).format(n);return(0,c.jsx)(s.A,{id:"theme.lastUpdated.atDate",description:"The words used to describe on which date a page has been last updated",values:{date:(0,c.jsx)("b",{children:(0,c.jsx)("time",{dateTime:n.toISOString(),itemProp:"dateModified",children:t})})},children:" on {date}"})}function f({lastUpdatedBy:e}){return(0,c.jsx)(s.A,{id:"theme.lastUpdated.byUser",description:"The words used to describe by who the page has been last updated",values:{user:(0,c.jsx)("b",{children:e})},children:" by {user}"})}function x({lastUpdatedAt:e,lastUpdatedBy:n}){return(0,c.jsxs)("span",{className:a.G.common.lastUpdated,children:[(0,c.jsx)(s.A,{id:"theme.lastUpdated.lastUpdatedAtBy",description:"The sentence used to display when a page has been last updated, and by who",values:{atDate:e?(0,c.jsx)(h,{lastUpdatedAt:e}):"",byUser:n?(0,c.jsx)(f,{lastUpdatedBy:n}):""},children:"Last updated{atDate}{byUser}"}),!1]})}const p="lastUpdated_JAkA",v="noPrint_WFHX";function j({className:e,editUrl:n,lastUpdatedAt:t,lastUpdatedBy:s}){return(0,c.jsxs)("div",{className:(0,i.A)("row",e),children:[(0,c.jsx)("div",{className:(0,i.A)("col",v),children:n&&(0,c.jsx)(d,{editUrl:n})}),(0,c.jsx)("div",{className:(0,i.A)("col",p),children:(t||s)&&(0,c.jsx)(x,{lastUpdatedAt:t,lastUpdatedBy:s})})]})}},3230(e,n,t){t.d(n,{A:()=>s});t(6540);var i=t(4848);function s(e){return(0,i.jsx)("code",{...e})}},5195(e,n,t){t.d(n,{A:()=>x});var i=t(6540),s=t(6342);function a(e){const n=e.map(e=>({...e,parentIndex:-1,children:[]})),t=Array(7).fill(-1);n.forEach((e,n)=>{const i=t.slice(2,e.level);e.parentIndex=Math.max(...i),t[e.level]=n});const i=[];return n.forEach(e=>{const{parentIndex:t,...s}=e;t>=0?n[t].children.push(s):i.push(s)}),i}function r({toc:e,minHeadingLevel:n,maxHeadingLevel:t}){return e.flatMap(e=>{const i=r({toc:e.children,minHeadingLevel:n,maxHeadingLevel:t});return function(e){return e.level>=n&&e.level<=t}(e)?[{...e,children:i}]:i})}function l(e){const n=e.getBoundingClientRect();return n.top===n.bottom?l(e.parentNode):n}function c(e,{anchorTopOffset:n}){const t=e.find(e=>l(e).top>=n);if(t){return function(e){return e.top>0&&e.bottom{e.current=n?0:document.querySelector(".navbar").clientHeight},[n]),e}function d(e){const n=(0,i.useRef)(void 0),t=o();(0,i.useEffect)(()=>{if(!e)return()=>{};const{linkClassName:i,linkActiveClassName:s,minHeadingLevel:a,maxHeadingLevel:r}=e;function l(){const e=function(e){return Array.from(document.getElementsByClassName(e))}(i),l=function({minHeadingLevel:e,maxHeadingLevel:n}){const t=[];for(let i=e;i<=n;i+=1)t.push(`h${i}.anchor`);return Array.from(document.querySelectorAll(t.join()))}({minHeadingLevel:a,maxHeadingLevel:r}),o=c(l,{anchorTopOffset:t.current}),d=e.find(e=>o&&o.id===function(e){return decodeURIComponent(e.href.substring(e.href.indexOf("#")+1))}(e));e.forEach(e=>{!function(e,t){t?(n.current&&n.current!==e&&n.current.classList.remove(s),e.classList.add(s),n.current=e):e.classList.remove(s)}(e,e===d)})}return document.addEventListener("scroll",l),document.addEventListener("resize",l),l(),()=>{document.removeEventListener("scroll",l),document.removeEventListener("resize",l)}},[e,t])}var u=t(8774),m=t(4848);function h({toc:e,className:n,linkClassName:t,isChild:i}){return e.length?(0,m.jsx)("ul",{className:i?void 0:n,children:e.map(e=>(0,m.jsxs)("li",{children:[(0,m.jsx)(u.A,{to:`#${e.id}`,className:t??void 0,dangerouslySetInnerHTML:{__html:e.value}}),(0,m.jsx)(h,{isChild:!0,toc:e.children,className:n,linkClassName:t})]},e.id))}):null}const f=i.memo(h);function x({toc:e,className:n="table-of-contents table-of-contents__left-border",linkClassName:t="table-of-contents__link",linkActiveClassName:l,minHeadingLevel:c,maxHeadingLevel:o,...u}){const h=(0,s.p)(),x=c??h.tableOfContents.minHeadingLevel,p=o??h.tableOfContents.maxHeadingLevel,v=function({toc:e,minHeadingLevel:n,maxHeadingLevel:t}){return(0,i.useMemo)(()=>r({toc:a(e),minHeadingLevel:n,maxHeadingLevel:t}),[e,n,t])}({toc:e,minHeadingLevel:x,maxHeadingLevel:p});return d((0,i.useMemo)(()=>{if(t&&l)return{linkClassName:t,linkActiveClassName:l,minHeadingLevel:x,maxHeadingLevel:p}},[t,l,x,p])),(0,m.jsx)(f,{toc:v,className:n,linkClassName:t,...u})}},6896(e,n,t){t.d(n,{A:()=>v});t(6540);var i=t(4164),s=t(1312),a=t(5260),r=t(4848);function l(){return(0,r.jsx)(s.A,{id:"theme.contentVisibility.unlistedBanner.title",description:"The unlisted content banner title",children:"Unlisted page"})}function c(){return(0,r.jsx)(s.A,{id:"theme.contentVisibility.unlistedBanner.message",description:"The unlisted content banner message",children:"This page is unlisted. Search engines will not index it, and only users having a direct link can access it."})}function o(){return(0,r.jsx)(a.A,{children:(0,r.jsx)("meta",{name:"robots",content:"noindex, nofollow"})})}function d(){return(0,r.jsx)(s.A,{id:"theme.contentVisibility.draftBanner.title",description:"The draft content banner title",children:"Draft page"})}function u(){return(0,r.jsx)(s.A,{id:"theme.contentVisibility.draftBanner.message",description:"The draft content banner message",children:"This page is a draft. It will only be visible in dev and be excluded from the production build."})}var m=t(7559),h=t(7293);function f({className:e}){return(0,r.jsx)(h.A,{type:"caution",title:(0,r.jsx)(d,{}),className:(0,i.A)(e,m.G.common.draftBanner),children:(0,r.jsx)(u,{})})}function x({className:e}){return(0,r.jsx)(h.A,{type:"caution",title:(0,r.jsx)(l,{}),className:(0,i.A)(e,m.G.common.unlistedBanner),children:(0,r.jsx)(c,{})})}function p(e){return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(o,{}),(0,r.jsx)(x,{...e})]})}function v({metadata:e}){const{unlisted:n,frontMatter:t}=e;return(0,r.jsxs)(r.Fragment,{children:[(n||t.unlisted)&&(0,r.jsx)(p,{}),t.draft&&(0,r.jsx)(f,{})]})}},7293(e,n,t){t.d(n,{A:()=>B});var i=t(6540),s=t(4848);function a(e){const{mdxAdmonitionTitle:n,rest:t}=function(e){const n=i.Children.toArray(e),t=n.find(e=>i.isValidElement(e)&&"mdxAdmonitionTitle"===e.type),a=n.filter(e=>e!==t),r=t?.props.children;return{mdxAdmonitionTitle:r,rest:a.length>0?(0,s.jsx)(s.Fragment,{children:a}):null}}(e.children),a=e.title??n;return{...e,...a&&{title:a},children:t}}var r=t(4164),l=t(1312),c=t(7559);const o="admonition_xJq3",d="admonitionHeading_Gvgb",u="admonitionIcon_Rf37",m="admonitionContent_BuS1";function h({type:e,className:n,children:t}){return(0,s.jsx)("div",{className:(0,r.A)(c.G.common.admonition,c.G.common.admonitionType(e),o,n),children:t})}function f({icon:e,title:n}){return(0,s.jsxs)("div",{className:d,children:[(0,s.jsx)("span",{className:u,children:e}),n]})}function x({children:e}){return e?(0,s.jsx)("div",{className:m,children:e}):null}function p(e){const{type:n,icon:t,title:i,children:a,className:r}=e;return(0,s.jsxs)(h,{type:n,className:r,children:[i||t?(0,s.jsx)(f,{title:i,icon:t}):null,(0,s.jsx)(x,{children:a})]})}function v(e){return(0,s.jsx)("svg",{viewBox:"0 0 14 16",...e,children:(0,s.jsx)("path",{fillRule:"evenodd",d:"M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"})})}const j={icon:(0,s.jsx)(v,{}),title:(0,s.jsx)(l.A,{id:"theme.admonition.note",description:"The default label used for the Note admonition (:::note)",children:"note"})};function g(e){return(0,s.jsx)(p,{...j,...e,className:(0,r.A)("alert alert--secondary",e.className),children:e.children})}function A(e){return(0,s.jsx)("svg",{viewBox:"0 0 12 16",...e,children:(0,s.jsx)("path",{fillRule:"evenodd",d:"M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"})})}const N={icon:(0,s.jsx)(A,{}),title:(0,s.jsx)(l.A,{id:"theme.admonition.tip",description:"The default label used for the Tip admonition (:::tip)",children:"tip"})};function b(e){return(0,s.jsx)(p,{...N,...e,className:(0,r.A)("alert alert--success",e.className),children:e.children})}function y(e){return(0,s.jsx)("svg",{viewBox:"0 0 14 16",...e,children:(0,s.jsx)("path",{fillRule:"evenodd",d:"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"})})}const C={icon:(0,s.jsx)(y,{}),title:(0,s.jsx)(l.A,{id:"theme.admonition.info",description:"The default label used for the Info admonition (:::info)",children:"info"})};function L(e){return(0,s.jsx)(p,{...C,...e,className:(0,r.A)("alert alert--info",e.className),children:e.children})}function H(e){return(0,s.jsx)("svg",{viewBox:"0 0 16 16",...e,children:(0,s.jsx)("path",{fillRule:"evenodd",d:"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"})})}const w={icon:(0,s.jsx)(H,{}),title:(0,s.jsx)(l.A,{id:"theme.admonition.warning",description:"The default label used for the Warning admonition (:::warning)",children:"warning"})};function T(e){return(0,s.jsx)("svg",{viewBox:"0 0 12 16",...e,children:(0,s.jsx)("path",{fillRule:"evenodd",d:"M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"})})}const U={icon:(0,s.jsx)(T,{}),title:(0,s.jsx)(l.A,{id:"theme.admonition.danger",description:"The default label used for the Danger admonition (:::danger)",children:"danger"})};const k={icon:(0,s.jsx)(H,{}),title:(0,s.jsx)(l.A,{id:"theme.admonition.caution",description:"The default label used for the Caution admonition (:::caution)",children:"caution"})};const _={...{note:g,tip:b,info:L,warning:function(e){return(0,s.jsx)(p,{...w,...e,className:(0,r.A)("alert alert--warning",e.className),children:e.children})},danger:function(e){return(0,s.jsx)(p,{...U,...e,className:(0,r.A)("alert alert--danger",e.className),children:e.children})}},...{secondary:e=>(0,s.jsx)(g,{title:"secondary",...e}),important:e=>(0,s.jsx)(L,{title:"important",...e}),success:e=>(0,s.jsx)(b,{title:"success",...e}),caution:function(e){return(0,s.jsx)(p,{...k,...e,className:(0,r.A)("alert alert--warning",e.className),children:e.children})}}};function B(e){const n=a(e),t=(i=n.type,_[i]||(console.warn(`No admonition component found for admonition type "${i}". Using Info as fallback.`),_.info));var i;return(0,s.jsx)(t,{...n})}},7763(e,n,t){t.d(n,{A:()=>l});t(6540);var i=t(4164),s=t(5195);const a="tableOfContents_bqdL";var r=t(4848);function l({className:e,...n}){return(0,r.jsx)("div",{className:(0,i.A)(a,"thin-scrollbar",e),children:(0,r.jsx)(s.A,{...n,linkClassName:"table-of-contents__link toc-highlight",linkActiveClassName:"table-of-contents__link--active"})})}},7910(e,n,t){t.d(n,{A:()=>r});t(6540);var i=t(8453),s=t(9057),a=t(4848);function r({children:e}){return(0,a.jsx)(i.x,{components:s.A,children:e})}},8453(e,n,t){t.d(n,{R:()=>r,x:()=>l});var i=t(6540);const s={},a=i.createContext(s);function r(e){const n=i.useContext(a);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),i.createElement(a.Provider,{value:n},e.children)}},8759(e,n,t){t.d(n,{A:()=>U});var i=t(6540),s=t(5260),a=t(8601),r=t(3230),l=t(4848);var c=t(4164),o=t(8774),d=t(3535);var u=t(3427),m=t(2303),h=t(1422);const f="details_lb9f",x="isBrowser_bmU9",p="collapsibleContent_i85q";function v(e){return!!e&&("SUMMARY"===e.tagName||v(e.parentElement))}function j(e,n){return!!e&&(e===n||j(e.parentElement,n))}function g({summary:e,children:n,...t}){(0,u.A)().collectAnchor(t.id);const s=(0,m.A)(),a=(0,i.useRef)(null),{collapsed:r,setCollapsed:o}=(0,h.u)({initialState:!t.open}),[d,g]=(0,i.useState)(t.open),A=i.isValidElement(e)?e:(0,l.jsx)("summary",{children:e??"Details"});return(0,l.jsxs)("details",{...t,ref:a,open:d,"data-collapsed":r,className:(0,c.A)(f,s&&x,t.className),onMouseDown:e=>{v(e.target)&&e.detail>1&&e.preventDefault()},onClick:e=>{e.stopPropagation();const n=e.target;v(n)&&j(n,a.current)&&(e.preventDefault(),r?(o(!1),g(!0)):o(!0))},children:[A,(0,l.jsx)(h.N,{lazy:!1,collapsed:r,onCollapseTransitionEnd:e=>{o(e),g(!e)},children:(0,l.jsx)("div",{className:p,children:n})})]})}const A="details_b_Ee";function N({...e}){return(0,l.jsx)(g,{...e,className:(0,c.A)("alert alert--info",A,e.className)})}function b(e){const n=i.Children.toArray(e.children),t=n.find(e=>i.isValidElement(e)&&"summary"===e.type),s=(0,l.jsx)(l.Fragment,{children:n.filter(e=>e!==t)});return(0,l.jsx)(N,{...e,summary:t,children:s})}var y=t(1107);function C(e){return(0,l.jsx)(y.A,{...e})}const L="containsTaskList_mC6p";function H(e){if(void 0!==e)return(0,c.A)(e,e?.includes("contains-task-list")&&L)}const w="img_ev3q";var T=t(7293);const U={Head:s.A,details:b,Details:b,code:function(e){return function(e){return void 0!==e.children&&i.Children.toArray(e.children).every(e=>"string"==typeof e&&!e.includes("\n"))}(e)?(0,l.jsx)(r.A,{...e}):(0,l.jsx)(a.h,{...e})},a:function(e){const n=(0,d.v)(e.id);return(0,l.jsx)(o.A,{...e,className:(0,c.A)(n,e.className)})},pre:function(e){return(0,l.jsx)(l.Fragment,{children:e.children})},ul:function(e){return(0,l.jsx)("ul",{...e,className:H(e.className)})},li:function(e){(0,u.A)().collectAnchor(e.id);const n=(0,d.v)(e.id);return(0,l.jsx)("li",{className:(0,c.A)(n,e.className),...e})},img:function(e){return(0,l.jsx)("img",{decoding:"async",loading:"lazy",...e,className:(n=e.className,(0,c.A)(n,w))});var n},h1:e=>(0,l.jsx)(C,{as:"h1",...e}),h2:e=>(0,l.jsx)(C,{as:"h2",...e}),h3:e=>(0,l.jsx)(C,{as:"h3",...e}),h4:e=>(0,l.jsx)(C,{as:"h4",...e}),h5:e=>(0,l.jsx)(C,{as:"h5",...e}),h6:e=>(0,l.jsx)(C,{as:"h6",...e}),admonition:T.A,mermaid:()=>null}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/597d88be.269bc776.js b/docs/assets/js/597d88be.269bc776.js
deleted file mode 100644
index 200aa4da9da..00000000000
--- a/docs/assets/js/597d88be.269bc776.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8715],{4190(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/firebase-realtime-database-alternative","title":"RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend","description":"Looking for a Firebase Realtime Database alternative? RxDB offers a fully offline, vendor-agnostic NoSQL solution with advanced conflict resolution and multi-platform support.","source":"@site/docs/articles/firebase-realtime-database-alternative.md","sourceDirName":"articles","slug":"/articles/firebase-realtime-database-alternative.html","permalink":"/articles/firebase-realtime-database-alternative.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend","slug":"firebase-realtime-database-alternative.html","description":"Looking for a Firebase Realtime Database alternative? RxDB offers a fully offline, vendor-agnostic NoSQL solution with advanced conflict resolution and multi-platform support."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB - Firestore Alternative to Sync with Your Own Backend","permalink":"/articles/firestore-alternative.html"},"next":{"title":"RxDB \u2013 The Ultimate Offline Database with Sync and Encryption","permalink":"/articles/offline-database.html"}}');var i=n(4848),o=n(8453);const a={title:"RxDB - Firebase Realtime Database Alternative to Sync With Your Own Backend",slug:"firebase-realtime-database-alternative.html",description:"Looking for a Firebase Realtime Database alternative? RxDB offers a fully offline, vendor-agnostic NoSQL solution with advanced conflict resolution and multi-platform support."},l="RxDB - The Firebase Realtime Database Alternative That Can Sync With Your Own Backend",t={},c=[{value:"Why RxDB Is an Excellent Firebase Realtime Database Alternative",id:"why-rxdb-is-an-excellent-firebase-realtime-database-alternative",level:2},{value:"1. Complete Offline-First Experience",id:"1-complete-offline-first-experience",level:3},{value:"2. Freedom to Use Any Server or Cloud",id:"2-freedom-to-use-any-server-or-cloud",level:3},{value:"3. Advanced Conflict Handling",id:"3-advanced-conflict-handling",level:3},{value:"4. Lower Cloud Costs for Read-Heavy Apps",id:"4-lower-cloud-costs-for-read-heavy-apps",level:3},{value:"5. Powerful Local Queries",id:"5-powerful-local-queries",level:3},{value:"6. True Offline Initialization",id:"6-true-offline-initialization",level:3},{value:"7. Works Everywhere JavaScript Runs",id:"7-works-everywhere-javascript-runs",level:3},{value:"How RxDB's Syncing Mechanism Operates",id:"how-rxdbs-syncing-mechanism-operates",level:2},{value:"Sample Code: Sync RxDB With a Custom Endpoint",id:"sample-code-sync-rxdb-with-a-custom-endpoint",level:2},{value:"Setting Up P2P Replication Over WebRTC",id:"setting-up-p2p-replication-over-webrtc",level:3},{value:"Quick Steps to Get Started",id:"quick-steps-to-get-started",level:2},{value:"Is RxDB the Right Solution for You?",id:"is-rxdb-the-right-solution-for-you",level:3}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"rxdb---the-firebase-realtime-database-alternative-that-can-sync-with-your-own-backend",children:"RxDB - The Firebase Realtime Database Alternative That Can Sync With Your Own Backend"})}),"\n",(0,i.jsxs)(s.p,{children:["Are you on the lookout for a ",(0,i.jsx)(s.strong,{children:"Firebase Realtime Database alternative"})," that gives you greater freedom, deeper offline capabilities, and allows you to seamlessly integrate with any backend? ",(0,i.jsx)(s.strong,{children:"RxDB"})," (Reactive Database) might be the perfect choice. This ",(0,i.jsx)(s.a,{href:"/articles/local-first-future.html",children:"local-first"}),", NoSQL data store runs entirely on the client while supporting real-time updates and robust syncing with any server environment\u2014making it a strong contender against Firebase Realtime Database's limitations and potential vendor lock-in."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"/files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"why-rxdb-is-an-excellent-firebase-realtime-database-alternative",children:"Why RxDB Is an Excellent Firebase Realtime Database Alternative"}),"\n",(0,i.jsx)(s.h3,{id:"1-complete-offline-first-experience",children:"1. Complete Offline-First Experience"}),"\n",(0,i.jsxs)(s.p,{children:["Unlike Firebase Realtime Database, which relies on central infrastructure to process data, RxDB is fully embedded within your client application (including ",(0,i.jsx)(s.a,{href:"/articles/browser-database.html",children:"browsers"}),", ",(0,i.jsx)(s.a,{href:"/nodejs-database.html",children:"Node.js"}),", ",(0,i.jsx)(s.a,{href:"/electron-database.html",children:"Electron"}),", and ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"}),"). This design means your app stays completely functional offline, since all data reads and writes happen locally. When connectivity is restored, RxDB's syncing framework automatically reconciles local changes with your remote backend."]}),"\n",(0,i.jsx)(s.h3,{id:"2-freedom-to-use-any-server-or-cloud",children:"2. Freedom to Use Any Server or Cloud"}),"\n",(0,i.jsx)(s.p,{children:"While Firebase Realtime Database ties you into Google's ecosystem, RxDB allows you to choose any hosting environment. You can:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Host your data on your own servers or private cloud."}),"\n",(0,i.jsxs)(s.li,{children:["Integrate with relational databases like ",(0,i.jsx)(s.a,{href:"/replication-http.html",children:"PostgreSQL"})," or other NoSQL options such as ",(0,i.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["Build custom endpoints using ",(0,i.jsx)(s.a,{href:"/replication-http.html",children:"REST"}),", ",(0,i.jsx)(s.a,{href:"/replication-graphql.html",children:"GraphQL"}),", or any other protocol."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"This flexibility ensures you're not locked into a single vendor and can adapt your backend strategy as your project evolves."}),"\n",(0,i.jsx)(s.h3,{id:"3-advanced-conflict-handling",children:"3. Advanced Conflict Handling"}),"\n",(0,i.jsxs)(s.p,{children:["Firebase Realtime Database typically updates data with a simple last-in-wins approach. RxDB, on the other hand, lets you implement more sophisticated conflict resolution logic. Using ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html#custom-conflict-handler",children:"revisions and conflict handlers"}),", RxDB can merge concurrent edits or preserve multiple versions\u2014ensuring your application remains consistent even when multiple clients modify the same data at the same time."]}),"\n",(0,i.jsx)(s.h3,{id:"4-lower-cloud-costs-for-read-heavy-apps",children:"4. Lower Cloud Costs for Read-Heavy Apps"}),"\n",(0,i.jsxs)(s.p,{children:["When you rely on Firebase Realtime Database, each query or listener can translate into ongoing reads, potentially running up your monthly bill. With RxDB, all queries are performed ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"locally"}),". Your app only communicates with the backend to sync document changes, significantly reducing bandwidth and hosting expenses for applications that frequently read data."]}),"\n",(0,i.jsx)(s.h3,{id:"5-powerful-local-queries",children:"5. Powerful Local Queries"}),"\n",(0,i.jsx)(s.p,{children:"If you've hit Firebase Realtime Database's querying limits, RxDB offers a far more robust approach to data retrieval. You can:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Define custom indexes for faster local lookups."}),"\n",(0,i.jsx)(s.li,{children:"Perform sophisticated filters, joins, or full-text searches right on the client."}),"\n",(0,i.jsxs)(s.li,{children:["Subscribe to real-time data updates through RxDB's ",(0,i.jsx)(s.a,{href:"/reactivity.html",children:"reactive query engine"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["Because these operations happen locally, your ",(0,i.jsx)(s.a,{href:"/articles/optimistic-ui.html",children:"UI updates"})," instantly, providing a snappy user experience."]}),"\n",(0,i.jsx)(s.h3,{id:"6-true-offline-initialization",children:"6. True Offline Initialization"}),"\n",(0,i.jsxs)(s.p,{children:["While Firebase offers some offline caching, it often requires an initial connection for authentication or to seed local data. RxDB, however, is built to handle an ",(0,i.jsx)(s.strong,{children:"offline-start"})," scenario. Users can begin working with the application immediately, regardless of connectivity, and any modifications they make will sync once the network is available again."]}),"\n",(0,i.jsx)(s.h3,{id:"7-works-everywhere-javascript-runs",children:"7. Works Everywhere JavaScript Runs"}),"\n",(0,i.jsxs)(s.p,{children:["One of RxDB's core strengths is its ability to run in ",(0,i.jsx)(s.strong,{children:"any JavaScript environment"}),". Whether you're building a web app that uses IndexedDB in the browser, an ",(0,i.jsx)(s.a,{href:"/electron-database.html",children:"Electron"})," desktop program, or a ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"})," mobile application, RxDB's ",(0,i.jsx)(s.strong,{children:"swappable storage"})," adapts to your runtime of choice. This consistency makes code-sharing and cross-platform development far simpler than being tied to a single backend system."]}),"\n",(0,i.jsx)(s.hr,{}),"\n",(0,i.jsx)(s.h2,{id:"how-rxdbs-syncing-mechanism-operates",children:"How RxDB's Syncing Mechanism Operates"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB employs its own ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Sync Engine"})," to manage data flow between your client and remote ",(0,i.jsx)(s.a,{href:"/rx-server.html",children:"servers"}),". Replication revolves around:"]}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Pull"}),": Retrieving updated or newly created documents from the server."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Push"}),": Sending local changes to the backend for persistence."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Live Updates"}),": Continuously streaming changes to and from the backend for real-time synchronization."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"sample-code-sync-rxdb-with-a-custom-endpoint",children:"Sample Code: Sync RxDB With a Custom Endpoint"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateRxCollection } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'localdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" eventReduce"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'task schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" complete"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'boolean'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Start a custom replication"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'custom-tasks-api'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // post local changes to your server"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" resp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'https://yourapi.com/tasks/push'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" method"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'POST'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" body"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ changes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docs })"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" resp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// return conflicting documents if any"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (lastCheckpoint"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // fetch new/updated items from your server"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `https://yourapi.com/tasks/pull?checkpoint="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastCheckpoint"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"&limit="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"setting-up-p2p-replication-over-webrtc",children:"Setting Up P2P Replication Over WebRTC"}),"\n",(0,i.jsx)(s.p,{children:"In addition to using a centralized backend, RxDB supports peer-to-peer synchronization through WebRTC, enabling devices to share data directly."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicateWebRTC"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getConnectionHandlerSimplePeer"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-webrtc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" webrtcPool"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateWebRTC"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" topic"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'p2p-topic-123'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" connectionHandlerCreator"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getConnectionHandlerSimplePeer"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" signalingServerUrl"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'wss://signaling.rxdb.info/'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" wrtc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'node-datachannel/polyfill'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" webSocketConstructor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'ws'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:").WebSocket"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"webrtcPool"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"error$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((error) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".error"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'P2P error:'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" error);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"Here, any client that joins the same topic communicates changes to other peers, all without requiring a traditional client-server model."}),"\n",(0,i.jsx)(s.h2,{id:"quick-steps-to-get-started",children:"Quick Steps to Get Started"}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsx)(s.li,{children:"Install RxDB"}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"})]})})})}),"\n",(0,i.jsxs)(s.ol,{start:"2",children:["\n",(0,i.jsx)(s.li,{children:"Create a Local Database"}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myLocalDB'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"Add a Collection"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"ts"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"Kopieren"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" notes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'notes schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLenght"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" content"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.ol,{start:"3",children:["\n",(0,i.jsx)(s.li,{children:"Synchronize"}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["Use one of the ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Replication Plugins"})," to connect with your preferred backend."]}),"\n",(0,i.jsx)(s.h3,{id:"is-rxdb-the-right-solution-for-you",children:"Is RxDB the Right Solution for You?"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Long Offline Use"}),": If your users need to work without an internet connection, RxDB's built-in offline-first design stands out compared to Firebase Realtime Database's partial offline approach."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Custom or Complex Queries"}),": RxDB lets you perform your ",(0,i.jsx)(s.a,{href:"/rx-query.html",children:"queries"})," locally, define ",(0,i.jsx)(s.a,{href:"/rx-schema.html#indexes",children:"indexing"}),", and handle even complex ",(0,i.jsx)(s.a,{href:"/rx-pipeline.html",children:"transformations"})," locally - no extra call to an external API."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Avoid Vendor Lock-In"}),": If you anticipate needing to move or adapt your backend later, you can do so without rewriting how your client manages its data."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Peer-to-Peer Collaboration"}),": Whether you need quick demos or real production use, ",(0,i.jsx)(s.a,{href:"/replication-webrtc.html",children:"WebRTC replication"})," can link your users directly without central coordination of data storage."]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/5a273530.2bec61c8.js b/docs/assets/js/5a273530.2bec61c8.js
deleted file mode 100644
index 5759e0344e5..00000000000
--- a/docs/assets/js/5a273530.2bec61c8.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3881],{4944(e,s,r){r.r(s),r.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>i,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"rx-storage-remote","title":"Remote RxStorage","description":"The Remote RxStorage is made to use a remote storage and communicate with it over an asynchronous message channel.","source":"@site/docs/rx-storage-remote.md","sourceDirName":".","slug":"/rx-storage-remote.html","permalink":"/rx-storage-remote.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Remote RxStorage","slug":"rx-storage-remote.html"},"sidebar":"tutorialSidebar","previous":{"title":"Logger \ud83d\udc51","permalink":"/logger.html"},"next":{"title":"Worker RxStorage \ud83d\udc51","permalink":"/rx-storage-worker.html"}}');var o=r(4848),a=r(8453);const i={title:"Remote RxStorage",slug:"rx-storage-remote.html"},t="Remote RxStorage",l={},c=[{value:"Usage",id:"usage",level:2},{value:"Usage with a Websocket server",id:"usage-with-a-websocket-server",level:2},{value:"Sending custom messages",id:"sending-custom-messages",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",strong:"strong",...(0,a.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"remote-rxstorage",children:"Remote RxStorage"})}),"\n",(0,o.jsxs)(s.p,{children:["The Remote ",(0,o.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," is made to use a remote storage and communicate with it over an asynchronous message channel.\nThe remote part could be on another JavaScript process or even on a different host machine.\nThe remote storage plugin is used in many RxDB plugins like the ",(0,o.jsx)(s.a,{href:"/rx-storage-worker.html",children:"worker"})," or the ",(0,o.jsx)(s.a,{href:"/electron.html",children:"electron"})," plugin."]}),"\n",(0,o.jsx)(s.h2,{id:"usage",children:"Usage"}),"\n",(0,o.jsxs)(s.p,{children:["The remote storage communicates over a message channel which has to implement the ",(0,o.jsx)(s.code,{children:"messageChannelCreator"})," function which returns an object that has a ",(0,o.jsx)(s.code,{children:"messages$"})," observable and a ",(0,o.jsx)(s.code,{children:"send()"})," function on both sides and a ",(0,o.jsx)(s.code,{children:"close()"})," function that closes the RemoteMessageChannel."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// on the client"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageRemote } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-remote'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageRemote"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-id'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mode"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'storage'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" messageChannelCreator"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".resolve"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" messages$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Subject"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" send"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(msg) {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // send to remote storage"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDb"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// on the remote"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { exposeRxStorageRemote } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-remote'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"exposeRxStorageRemote"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" messages$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Subject"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" send"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(msg){"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // send to other side"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"usage-with-a-websocket-server",children:"Usage with a Websocket server"}),"\n",(0,o.jsxs)(s.p,{children:["The remote storage plugin contains helper functions to create a remote storage over a WebSocket server.\nThis is often used in Node.js to give one microservice access to another services database ",(0,o.jsx)(s.strong,{children:"without"})," having to replicate the full database state."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// server.js"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageMemory } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-memory'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { startRxStorageRemoteWebsocketServer } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-remote-websocket'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// either you can create the server based on a RxDatabase"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" serverBasedOnDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" startRxStorageRemoteWebsocketServer"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" port"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 8080"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" myRxDatabase"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// or you can create the server based on a pure RxStorage"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" serverBasedOn"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" startRxStorageRemoteWebsocketServer"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" port"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 8080"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMemory"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// client.js"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageRemoteWebsocket } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-remote-websocket'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDb"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageRemoteWebsocket"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'ws://example.com:8080'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"sending-custom-messages",children:"Sending custom messages"}),"\n",(0,o.jsx)(s.p,{children:"The remote storage can also be used to send custom messages to and from the remote instance."}),"\n",(0,o.jsxs)(s.p,{children:["One the remote you have to define a ",(0,o.jsx)(s.code,{children:"customRequestHandler"})," like:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" serverBasedOnDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" startRxStorageRemoteWebsocketServer"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" port"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 8080"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" myRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" customRequestHandler"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(msg){"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // here you can return any JSON object as an 'answer'"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foo"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } "})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsxs)(s.p,{children:["On the client instance you can then call the ",(0,o.jsx)(s.code,{children:"customRequest()"})," method:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageRemoteWebsocket"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'ws://example.com:8080'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" answer"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".customRequest"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ bar"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(answer); "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > { foo: 'bar' }"})]})]})})})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453(e,s,r){r.d(s,{R:()=>i,x:()=>t});var n=r(6540);const o={},a=n.createContext(o);function i(e){const s=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),n.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/5b5afcec.7e369a0e.js b/docs/assets/js/5b5afcec.7e369a0e.js
deleted file mode 100644
index 804b9544b99..00000000000
--- a/docs/assets/js/5b5afcec.7e369a0e.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3633],{5255(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>i,toc:()=>d});const i=JSON.parse('{"id":"articles/json-based-database","title":"JSON-Based Databases - Why NoSQL and RxDB Simplify App Development","description":"Dive into how JSON-based databases power modern UI-centric apps, why NoSQL often outperforms SQL for dynamic data, how SQLite accommodates JSON, and why RxDB delivers a seamless offline-first solution for JavaScript with advanced JSON features.","source":"@site/docs/articles/json-based-database.md","sourceDirName":"articles","slug":"/articles/json-based-database.html","permalink":"/articles/json-based-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"JSON-Based Databases - Why NoSQL and RxDB Simplify App Development","slug":"json-based-database.html","description":"Dive into how JSON-based databases power modern UI-centric apps, why NoSQL often outperforms SQL for dynamic data, how SQLite accommodates JSON, and why RxDB delivers a seamless offline-first solution for JavaScript with advanced JSON features."},"sidebar":"tutorialSidebar","previous":{"title":"IndexedDB Max Storage Size Limit - Detailed Best Practices","permalink":"/articles/indexeddb-max-storage-limit.html"},"next":{"title":"ReactJS Storage - From Basic LocalStorage to Advanced Offline Apps with RxDB","permalink":"/articles/reactjs-storage.html"}}');var r=n(4848),a=n(8453);const t={title:"JSON-Based Databases - Why NoSQL and RxDB Simplify App Development",slug:"json-based-database.html",description:"Dive into how JSON-based databases power modern UI-centric apps, why NoSQL often outperforms SQL for dynamic data, how SQLite accommodates JSON, and why RxDB delivers a seamless offline-first solution for JavaScript with advanced JSON features."},o="JSON-Based Databases: Why NoSQL and RxDB Simplify App Development",l={},d=[{value:"Why JSON-Based Databases Are Typically NoSQL",id:"why-json-based-databases-are-typically-nosql",level:2},{value:"Document-Oriented by Nature",id:"document-oriented-by-nature",level:3},{value:"Flexible, Schema-Agnostic",id:"flexible-schema-agnostic",level:3},{value:"Aligned With Evolving User Interfaces",id:"aligned-with-evolving-user-interfaces",level:3},{value:"Is NoSQL \u201cBetter\u201d Than SQL?",id:"is-nosql-better-than-sql",level:2},{value:"When to Prefer SQL Instead of JSON/NoSQL",id:"when-to-prefer-sql-instead-of-jsonnosql",level:2},{value:"Storing JSON in Traditional SQL Databases",id:"storing-json-in-traditional-sql-databases",level:2},{value:"JSON Columns in PostgreSQL or MySQL",id:"json-columns-in-postgresql-or-mysql",level:3},{value:"Storing JSON in SQLite",id:"storing-json-in-sqlite",level:2},{value:"JSON vs. Database - Why a Plain JSON Text File is a Problem",id:"json-vs-database---why-a-plain-json-text-file-is-a-problem",level:2},{value:"RxDB: A JSON-Focused Database for JavaScript Apps",id:"rxdb-a-json-focused-database-for-javascript-apps",level:2},{value:"Key Characteristics",id:"key-characteristics",level:3},{value:"Advanced JSON Features in RxDB",id:"advanced-json-features-in-rxdb",level:3},{value:"Follow Up",id:"follow-up",level:2}];function c(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(s.header,{children:(0,r.jsx)(s.h1,{id:"json-based-databases-why-nosql-and-rxdb-simplify-app-development",children:"JSON-Based Databases: Why NoSQL and RxDB Simplify App Development"})}),"\n",(0,r.jsxs)(s.p,{children:["Modern applications handle highly dynamic, often deeply nested data structures\u2014commonly represented in ",(0,r.jsx)(s.strong,{children:"JSON"}),". Whether you're building a real-time dashboard or a fully offline mobile app, storing and querying data in a JSON-friendly way can reduce overhead and coding complexity. This is where ",(0,r.jsx)(s.strong,{children:"JSON-based databases"})," (often part of the ",(0,r.jsx)(s.strong,{children:"NoSQL"})," family) come into play, letting you store objects in the same format they're used in your code, eliminating the schema wrangling that can come with a strict relational design."]}),"\n",(0,r.jsxs)(s.p,{children:["Below, we explore why JSON-based databases naturally align with ",(0,r.jsx)(s.strong,{children:"NoSQL"})," principles, how relational engines (like PostgreSQL or SQLite) handle JSON columns, the pitfalls of storing data in a single plain JSON text file, and the ways ",(0,r.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," stands out as an offline-first JSON solution for JavaScript developers\u2014complete with advanced features like JSON-Schema and JSON-key-compression."]}),"\n",(0,r.jsx)("p",{align:"center",children:(0,r.jsx)("img",{src:"/files/no-sql.png",alt:"NoSQL",width:"100"})}),"\n",(0,r.jsx)(s.h2,{id:"why-json-based-databases-are-typically-nosql",children:"Why JSON-Based Databases Are Typically NoSQL"}),"\n",(0,r.jsx)(s.h3,{id:"document-oriented-by-nature",children:"Document-Oriented by Nature"}),"\n",(0,r.jsxs)(s.p,{children:["When your data is stored as JSON, each record or document can hold nested arrays and sub-objects with no forced table schema. NoSQL solutions such as ",(0,r.jsx)(s.a,{href:"/rx-storage-mongodb.html",children:"MongoDB"}),", ",(0,r.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB"}),", ",(0,r.jsx)(s.a,{href:"/replication-firestore.html",children:"Firebase"}),", and ",(0,r.jsx)(s.strong,{children:"RxDB"})," store and retrieve these documents in their \u201craw\u201d JSON form. This model integrates smoothly with how front-end applications already handle data, minimizing transformations and improving developer productivity."]}),"\n",(0,r.jsx)(s.h3,{id:"flexible-schema-agnostic",children:"Flexible, Schema-Agnostic"}),"\n",(0,r.jsx)(s.p,{children:"Traditional SQL tables enforce rigid column definitions and demand explicit schema migrations when you add or rename a field. By contrast, NoSQL solutions accept more dynamic data structures, allowing changes on the fly. This means a front-end developer can add a new field to a JSON object\u2014perhaps for a new feature\u2014without the friction of redefining or migrating a database schema. While this is possible, it is often not recommended."}),"\n",(0,r.jsx)(s.h3,{id:"aligned-with-evolving-user-interfaces",children:"Aligned With Evolving User Interfaces"}),"\n",(0,r.jsx)(s.p,{children:"As modern UIs frequently manipulate deeply nested or changing data, developers find it easier to store whole objects directly, saving time that might otherwise be spent performing complex joins or normalizing data. For instance, frameworks like React, Vue, or Angular are inherently comfortable with nested JSON structures, which map more directly to NoSQL\u2019s \u201cdocument\u201d approach than to relational tables."}),"\n",(0,r.jsx)(s.h2,{id:"is-nosql-better-than-sql",children:"Is NoSQL \u201cBetter\u201d Than SQL?"}),"\n",(0,r.jsxs)(s.p,{children:["It depends on your application. ",(0,r.jsx)(s.strong,{children:"SQL"})," remains exceptional for complex aggregations, enforced relationships, and sophisticated transaction handling. But ",(0,r.jsx)(s.strong,{children:"NoSQL"})," is often more intuitive and easier to maintain for \u201cdocument-first\u201d applications that:"]}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsx)(s.li,{children:"Thrive on flexible or rapidly evolving data models."}),"\n",(0,r.jsx)(s.li,{children:"Rely on hierarchical or nested JSON objects."}),"\n",(0,r.jsx)(s.li,{children:"Avoid multi-table joins."}),"\n",(0,r.jsx)(s.li,{children:"Require easy horizontal scaling for large sets of documents."}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"Relational databases are still a top choice for many enterprise back-ends, especially when advanced analytics or strongly enforced referential integrity is needed. But if your application is predominantly storing and manipulating JSON documents (e.g., user profiles, real-time chat logs, embedded items), a JSON-based or document-oriented approach can greatly reduce friction during development."}),"\n",(0,r.jsx)(s.h2,{id:"when-to-prefer-sql-instead-of-jsonnosql",children:"When to Prefer SQL Instead of JSON/NoSQL"}),"\n",(0,r.jsxs)(s.p,{children:["NoSQL solutions\u2014particularly JSON-based document stores\u2014provide a natural fit for flexible, nested data in UI-heavy applications. However, certain scenarios may benefit more from a ",(0,r.jsx)(s.strong,{children:"SQL"})," solution:"]}),"\n",(0,r.jsxs)(s.ol,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Complex Relationships"}),": If your data demands intricate joins across multiple entities (e.g., many-to-many relationships that can\u2019t easily be embedded in a single document), a well-structured relational schema can simplify queries."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Strong Integrity and Constraints"}),": SQL excels at enforcing constraints such as foreign keys, unique constraints, and advanced triggers. If your system needs strict data validation and complex business logic within the database, SQL might prove more robust."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"High-End Analytical Queries"}),": Relational databases can handle sophisticated aggregations, groupings, and joins more efficiently. If your app frequently runs advanced SQL queries, a NoSQL approach may complicate or slow down analytics."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Legacy Integration"}),": Many enterprise systems are built around existing relational schemas. A purely NoSQL approach might mean rewriting or bridging systems that are heavily reliant on SQL constraints and transformations."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Transaction Handling"}),": While many NoSQL solutions have improved transaction support, it can still lag behind well-established SQL transaction models. If ACID properties and multi-operation atomicity are paramount, you might prefer a tried-and-true relational engine."]}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"In short, if you prioritize advanced relational queries, robust constraints, or complex business rules at the database level, SQL remains a powerful, and possibly superior, choice. For user-centric, fast-evolving JSON data, though, NoSQL or JSON-based solutions often reduce the friction of frequent schema changes."}),"\n",(0,r.jsx)(s.h2,{id:"storing-json-in-traditional-sql-databases",children:"Storing JSON in Traditional SQL Databases"}),"\n",(0,r.jsx)(s.h3,{id:"json-columns-in-postgresql-or-mysql",children:"JSON Columns in PostgreSQL or MySQL"}),"\n",(0,r.jsxs)(s.p,{children:["To accommodate the demand for flexible data, several SQL engines (notably ",(0,r.jsx)(s.strong,{children:"PostgreSQL"})," and MySQL) introduced support for ",(0,r.jsx)(s.strong,{children:"JSON"})," columns. PostgreSQL offers the ",(0,r.jsx)(s.code,{children:"JSON"})," and ",(0,r.jsx)(s.code,{children:"JSONB"})," types, enabling developers to store raw JSON in a column. You can also index specific paths within the JSON to speed lookups on nested fields:"]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"sql","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"sql","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"CREATE"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" TABLE"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" products"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"SERIAL"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" PRIMARY KEY"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" name"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" TEXT"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" details JSONB"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"-- Insert a record with JSON data"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"INSERT INTO"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" products ("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"name"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:", details) "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"VALUES"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Laptop'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:", "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'\'{"brand": "BrandX", "features": ["Touchscreen", "SSD"]}\''}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,r.jsx)(s.p,{children:"Although this approach merges the best of both worlds (SQL queries + flexible JSON fields), it can also create a \u201csplit personality\u201d in your schema. You might store stable data in normal columns, while unpredictable or nested details live inside a JSONB field. Some projects flourish with this hybrid design, others find it a bit unwieldy."}),"\n",(0,r.jsx)("center",{children:(0,r.jsx)("img",{src:"../files/icons/sqlite.svg",alt:"WASM SQLite",width:"140",class:"img-padding"})}),"\n",(0,r.jsx)(s.h2,{id:"storing-json-in-sqlite",children:"Storing JSON in SQLite"}),"\n",(0,r.jsxs)(s.p,{children:["SQLite also allows storing JSON data, typically as text columns, but with some additional features since ",(0,r.jsx)(s.strong,{children:"SQLite 3.9"})," (2015) including the ",(0,r.jsx)(s.a,{href:"https://www.sqlite.org/json1.html",children:"JSON1 extension"}),". This extension can parse JSON text, perform queries on JSON fields, and do partial updates. However, storing JSON in SQLite does require you to ensure you\u2019ve compiled SQLite with JSON1 support or to rely on a library that bundles it. While possible, you still won't get quite the same schema-agnostic ease as a full document store, but it\u2019s a pragmatic solution for smaller or embedded needs on the server side\u2014or occasionally in the browser if you run SQLite via WebAssembly. RxDB uses this in its ",(0,r.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite storage"}),"."]}),"\n",(0,r.jsx)(s.h2,{id:"json-vs-database---why-a-plain-json-text-file-is-a-problem",children:"JSON vs. Database - Why a Plain JSON Text File is a Problem"}),"\n",(0,r.jsx)(s.p,{children:"Some developers consider storing everything in a single JSON file, typically read and written directly from disk or local storage. This approach, while seemingly simple, usually does not scale. Key issues include:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"No Concurrency"}),": If multiple parts of the application try to write to the same JSON file, you risk overwriting changes."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"No Indexes"}),": Finding or filtering items in large JSON text requires scanning everything. This is slow and quickly becomes unmanageable."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"No Partial Updates"}),": You often reload the entire file, modify it in memory, then write it back, which is highly inefficient for large data sets."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"Corruption Risk"}),": A single corrupted write or partial save might break the entire JSON file, losing all data."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.strong,{children:"High Memory Usage"}),": The entire file may need to be parsed into memory, even if you only need a fraction of the data."]}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"Databases\u2014relational or NoSQL\u2014solve these issues by handling concurrency, enabling partial reads/writes, establishing indexes, and ensuring transactional integrity so you don\u2019t lose everything if the process is interrupted mid-write."}),"\n",(0,r.jsx)("center",{children:(0,r.jsx)("a",{href:"https://rxdb.info/",children:(0,r.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript JSON Database",width:"220"})})}),"\n",(0,r.jsx)(s.h2,{id:"rxdb-a-json-focused-database-for-javascript-apps",children:"RxDB: A JSON-Focused Database for JavaScript Apps"}),"\n",(0,r.jsxs)(s.p,{children:["Many NoSQL databases operate on the server, whereas RxDB is built for client-side usage\u2014browsers, mobile apps, or Node.js. It specializes in JSON documents and embraces an ",(0,r.jsx)(s.a,{href:"/offline-first.html",children:"offline-first"})," philosophy."]}),"\n",(0,r.jsx)(s.h3,{id:"key-characteristics",children:"Key Characteristics"}),"\n",(0,r.jsxs)(s.ol,{children:["\n",(0,r.jsx)(s.li,{children:(0,r.jsx)(s.strong,{children:"Local JSON Storage"})}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"RxDB stores each record as a JSON document, closely matching how front-end frameworks handle state. This eliminates complex transformations or manual JSON parsing before writing to a table."}),"\n",(0,r.jsxs)(s.ol,{start:"2",children:["\n",(0,r.jsx)(s.li,{children:(0,r.jsx)(s.strong,{children:"Reactive Queries"})}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["Instead of complex SQL, RxDB uses JSON-based ",(0,r.jsx)(s.a,{href:"/rx-query.html",children:"query"})," definitions. You can subscribe to query results, letting your UI automatically refresh when data changes locally or from remote sync updates:"]}),"\n",(0,r.jsx)("p",{align:"center",children:(0,r.jsx)("img",{src:"../files/animations/realtime.gif",alt:"realtime ui updates",width:"700"})}),"\n",(0,r.jsxs)(s.ol,{start:"3",children:["\n",(0,r.jsx)(s.li,{children:(0,r.jsx)(s.strong,{children:"Offline-First Sync"})}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"Built-in replication plugins push/pull changes to or from a remote server. If your app is offline, updates get stored locally, then sync up seamlessly once a connection is available."}),"\n",(0,r.jsxs)(s.ol,{start:"4",children:["\n",(0,r.jsx)(s.li,{children:(0,r.jsx)(s.strong,{children:"Optional JSON-Schema"})}),"\n"]}),"\n",(0,r.jsx)(s.p,{children:"Though it\u2019s a document database, RxDB encourages you to define a JSON-based schema for clarity, indexing, and type validation. This helps maintain data consistency while still allowing a measure of flexibility for new fields."}),"\n",(0,r.jsx)(s.h3,{id:"advanced-json-features-in-rxdb",children:"Advanced JSON Features in RxDB"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"JSON-Schema"}),": By specifying a JSON-Schema, you can define which fields exist, whether they are required, and their data types. This is invaluable for catching malformed documents early and imposing mild structure in a NoSQL setting."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"JSON Key-Compression"}),": Large, verbose field names can bloat storage usage. RxDB\u2019s optional ",(0,r.jsx)(s.a,{href:"/key-compression.html",children:"key-compression plugin"})," automatically shortens field names in your JSON documents internally, reducing disk space and bandwidth:"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Example: how key-compression can transform your documents"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" uncompressed"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "firstName"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Corrine"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "lastName"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Ziemann"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "shoppingCartItems"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"productNumber"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 29857"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "amount"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"productNumber"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 53409"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "amount"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 6"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" compressed"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|e"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Corrine"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|g"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Ziemann"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|i"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"|h"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 29857"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|b"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"|h"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 53409"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "|b"'}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 6"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,r.jsx)(s.p,{children:"The user sees no difference in their code\u2014RxDB automatically decompresses data on read\u2014but the overhead is drastically reduced behind the scenes."}),"\n",(0,r.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,r.jsx)(s.p,{children:"JSON-based databases naturally align with NoSQL because they accommodate evolving, nested data without rigid schemas. This makes them appealing for many UI-centric or offline-first applications where flexible documents and agile development cycles matter more than heavy relational queries or constraints."}),"\n",(0,r.jsx)(s.p,{children:"SQL can still store JSON\u2014whether in PostgreSQL\u2019s JSONB columns, MySQL\u2019s JSON fields, or SQLite\u2019s JSON1 extension. For some teams, a hybrid approach pairing SQL for relational data with JSON columns for more flexible fields works well. However, storing everything in a single monolithic JSON text file is rarely advisable for anything beyond trivial tasks\u2014databases excel at concurrency, indexing, and partial writes."}),"\n",(0,r.jsxs)(s.p,{children:["Tools like RxDB provide an even simpler, ",(0,r.jsx)(s.a,{href:"/articles/local-first-future.html",children:"local-first"})," take on JSON documents\u2014particularly for JavaScript projects. With offline ",(0,r.jsx)(s.a,{href:"/replication.html",children:"replication"}),", reactive queries, optional JSON-Schema, and advanced optimizations such as key-compression, RxDB streamlines building dynamic, user-facing features while preserving the core benefits of a robust document database."]}),"\n",(0,r.jsx)(s.p,{children:"To explore more about RxDB and its capabilities for browser database development, check out the following resources:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.a,{href:"/code/",children:"RxDB GitHub Repository"}),": Visit the official GitHub repository of RxDB to access the source code, documentation, and community support."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),": Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"RxDB Examples"}),": Browse official examples to see RxDB in action and learn best practices you can apply to your own project - even if jQuery isn't explicitly featured, the patterns are similar."]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,r.jsx)(s,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},8453(e,s,n){n.d(s,{R:()=>t,x:()=>o});var i=n(6540);const r={},a=i.createContext(r);function t(e){const s=i.useContext(a);return i.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function o(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:t(e.components),i.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/5e95c892.e42e7000.js b/docs/assets/js/5e95c892.e42e7000.js
deleted file mode 100644
index 8275f2ca12c..00000000000
--- a/docs/assets/js/5e95c892.e42e7000.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9647],{7121(e,r,s){s.r(r),s.d(r,{default:()=>n});s(6540);var a=s(4164),u=s(7559),c=s(5500),l=s(2831),d=s(8711),h=s(4848);function n(e){return(0,h.jsx)(c.e3,{className:(0,a.A)(u.G.wrapper.docsPages),children:(0,h.jsx)(d.A,{children:(0,l.v)(e.route.routes)})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/61792630.6e389211.js b/docs/assets/js/61792630.6e389211.js
deleted file mode 100644
index fc1b3c322dd..00000000000
--- a/docs/assets/js/61792630.6e389211.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7277],{3247(e,s,n){n.d(s,{g:()=>i});var r=n(4848);function i(e){const s=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&s.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&s.push(n),(0,r.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:s+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,r.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5270(e,s,n){n.r(s),n.d(s,{assets:()=>c,contentTitle:()=>t,default:()=>p,frontMatter:()=>l,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"articles/angular-indexeddb","title":"Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone","description":"Discover how to harness IndexedDB in Angular with RxDB for robust offline apps. Learn reactive queries, advanced features, and more.","source":"@site/docs/articles/angular-indexeddb.md","sourceDirName":"articles","slug":"/articles/angular-indexeddb.html","permalink":"/articles/angular-indexeddb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone","slug":"angular-indexeddb.html","description":"Discover how to harness IndexedDB in Angular with RxDB for robust offline apps. Learn reactive queries, advanced features, and more."},"sidebar":"tutorialSidebar","previous":{"title":"What Really Is a Realtime Database?","permalink":"/articles/realtime-database.html"},"next":{"title":"IndexedDB Database in React Apps - The Power of RxDB","permalink":"/articles/react-indexeddb.html"}}');var i=n(4848),o=n(8453),a=n(2636);n(3247);const l={title:"Build Smarter Offline-First Angular Apps - How RxDB Beats IndexedDB Alone",slug:"angular-indexeddb.html",description:"Discover how to harness IndexedDB in Angular with RxDB for robust offline apps. Learn reactive queries, advanced features, and more."},t="Build Smarter Offline-First Angular Apps: How RxDB Beats IndexedDB Alone",c={},d=[{value:"What Is IndexedDB?",id:"what-is-indexeddb",level:2},{value:"Why Use IndexedDB in Angular",id:"why-use-indexeddb-in-angular",level:2},{value:"Why Using Plain IndexedDB is a Problem",id:"why-using-plain-indexeddb-is-a-problem",level:2},{value:"Set Up RxDB in Angular",id:"set-up-rxdb-in-angular",level:2},{value:"Installing RxDB",id:"installing-rxdb",level:3},{value:"Patch Change Detection with zone.js",id:"patch-change-detection-with-zonejs",level:3},{value:"Create a Database and Collections",id:"create-a-database-and-collections",level:3},{value:"Localstorage",id:"localstorage",level:3},{value:"IndexedDB",id:"indexeddb",level:3},{value:"CRUD Operations",id:"crud-operations",level:3},{value:"Reactive Queries and Live Updates",id:"reactive-queries-and-live-updates",level:2},{value:"With RxJS Observables and Async Pipes",id:"with-rxjs-observables-and-async-pipes",level:3},{value:"With Angular Signals",id:"with-angular-signals",level:3},{value:"Angular IndexedDB Example with RxDB",id:"angular-indexeddb-example-with-rxdb",level:2},{value:"Advanced RxDB Features",id:"advanced-rxdb-features",level:2},{value:"Limitations of IndexedDB",id:"limitations-of-indexeddb",level:2},{value:"Alternatives to IndexedDB",id:"alternatives-to-indexeddb",level:2},{value:"Performance comparison with other browser storages",id:"performance-comparison-with-other-browser-storages",level:2},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"build-smarter-offline-first-angular-apps-how-rxdb-beats-indexeddb-alone",children:"Build Smarter Offline-First Angular Apps: How RxDB Beats IndexedDB Alone"})}),"\n",(0,i.jsxs)(s.p,{children:["In modern web applications, offline capabilities and fast interactions are crucial. IndexedDB, the ",(0,i.jsx)(s.a,{href:"/articles/browser-database.html",children:"browser"}),"'s built-in database, allows you to store data locally, making your Angular application more robust and responsive. However, IndexedDB can be cumbersome to work with directly. That's where RxDB (Reactive Database) shines. In this article, we'll walk you through how to utilize IndexedDB in your Angular project using ",(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," as a convenient abstraction layer."]}),"\n",(0,i.jsx)(s.h2,{id:"what-is-indexeddb",children:"What Is IndexedDB?"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API",children:"IndexedDB"})," is a low-level JavaScript API for client-side storage of large amounts of structured data. It allows you to create key-value or object store-based data storage right in the user's browser. IndexedDB supports transactions and indexing but lacks a robust query API and can be complex to use due to its callback-based nature."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("img",{src:"../files/icons/angular.svg",alt:"Angular IndexedDB",width:"120"})}),"\n",(0,i.jsx)(s.h2,{id:"why-use-indexeddb-in-angular",children:"Why Use IndexedDB in Angular"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/offline-first.html",children:"Offline-First"}),"/",(0,i.jsx)(s.a,{href:"/articles/local-first-future.html",children:"Local-First"}),": If your app needs to function with limited or no internet connectivity, IndexedDB provides a reliable local storage layer. Users can continue using the application offline, and data can sync when the connection is restored."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Performance"}),": Local data access comes with ",(0,i.jsx)(s.a,{href:"/articles/zero-latency-local-first.html",children:"near-zero latency"}),", removing the need for constant server requests and eliminating most loading spinners."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Easier to Implement"}),": By replicating all necessary data to the client once, you avoid implementing numerous backend endpoints for each user interaction."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Scalability"}),": Local data queries remove processing load from your servers and reduce bandwidth usage by handling queries on the client side."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"why-using-plain-indexeddb-is-a-problem",children:"Why Using Plain IndexedDB is a Problem"}),"\n",(0,i.jsx)(s.p,{children:"Despite the advantages, directly working with IndexedDB has several drawbacks:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Callback-Based"}),": IndexedDB was originally designed around a callback-based API, which can be unwieldy compared to modern Promise or RxJS-based flows."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Difficult to Implement"}),': IndexedDB is often described as a "low-level" API. It\'s more suitable for library authors rather than application developers who simply need a robust local store.']}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Rudimentary Query API"}),": Complex or dynamic queries are cumbersome with IndexedDB's basic get/put approach and limited indexes."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"TypeScript Support"}),": Maintaining strong TypeScript types for all document structures is not straightforward with IndexedDB's untyped object stores."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"No Observable API"}),": IndexedDB cannot directly emit live data changes. With RxDB, you can subscribe to changes on a collection or even a single document field."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Cross-Tab Synchronization"}),": Handling concurrent data changes across multiple browser tabs is difficult in IndexedDB. RxDB has built-in multi-tab support that keeps all tabs in sync."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Advanced Features Missing"}),": IndexedDB lacks built-in support for encryption, compression, or other advanced data management features."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Browser-Only"}),": IndexedDB works in the browser but not in environments like React Native or Electron. RxDB offers storage adapters to seamlessly reuse the same code on different platforms."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"set-up-rxdb-in-angular",children:"Set Up RxDB in Angular"}),"\n",(0,i.jsx)(s.h3,{id:"installing-rxdb",children:"Installing RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["You can ",(0,i.jsx)(s.a,{href:"/install.html",children:"install RxDB"})," into your Angular application via npm:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" --save"})]})})})}),"\n",(0,i.jsx)(s.h3,{id:"patch-change-detection-with-zonejs",children:"Patch Change Detection with zone.js"}),"\n",(0,i.jsx)(s.p,{children:"RxDB creates RxJS observables outside of Angular's zone, meaning Angular won't automatically trigger change detection when new data arrives. You must patch RxJS with zone.js:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> app.component.ts"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * IMPORTANT: RxDB creates rxjs observables outside of Angular's zone"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * So you have to import the rxjs patch to ensure change detection works correctly."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" https://www.bennadel.com/blog/3448-binding-rxjs-observable-sources-outside-of-the-ngzone-in-angular-6-0-2.htm"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'zone.js/plugins/zone-patch-rxjs'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),"\n",(0,i.jsx)(s.h3,{id:"create-a-database-and-collections",children:"Create a Database and Collections"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB supports multiple storage options. The free and simple approach is using the ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"localstorage-based"})," storage. For higher performance, there's a premium plain ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB storage"}),"."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Define your schema"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'hero schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" description"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Describes a hero in your app'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsxs)(a.t,{children:[(0,i.jsx)(s.h3,{id:"localstorage",children:"Localstorage"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Create a database"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // the name of the database"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Add collections"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroSchema"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),(0,i.jsx)(s.h3,{id:"indexeddb",children:"IndexedDB"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Create a database"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // the name of the database"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Add collections"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroSchema"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})})]}),"\n",(0,i.jsxs)(s.p,{children:["It's recommended to encapsulate database creation logic in an Angular service, such as in a DatabaseService. A full example is available in ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/master/examples/angular/src/app/services/database.service.ts",children:"RxDB's Angular example"}),"."]}),"\n",(0,i.jsx)(s.h3,{id:"crud-operations",children:"CRUD Operations"}),"\n",(0,i.jsx)(s.p,{children:"Once your database is initialized, you can perform all CRUD operations:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Iron Man'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Genius-level intellect'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// bulk insert"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".bulkInsert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Thor'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'God of Thunder'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Hulk'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Superhuman Strength'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]);"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// find and findOne"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" ironMan"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Iron Man'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// update"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Hulk'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".update"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ $set"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Unlimited Strength'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } });"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// delete"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Thor'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"reactive-queries-and-live-updates",children:"Reactive Queries and Live Updates"}),"\n",(0,i.jsxs)(s.p,{children:["A key benefit of RxDB is reactivity. You can subscribe to changes and have your UI automatically reflect updates in ",(0,i.jsx)(s.a,{href:"/articles/realtime-database.html",children:"real time"})," even across browser tabs."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/animations/realtime.gif",alt:"realtime ui updates",width:"700"})}),"\n",(0,i.jsx)(s.h3,{id:"with-rxjs-observables-and-async-pipes",children:"With RxJS Observables and Async Pipes"}),"\n",(0,i.jsxs)(s.p,{children:["In Angular, you can display this data with the ",(0,i.jsx)(s.code,{children:"AsyncPipe"}),":"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"constructor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(private dbService: DatabaseService) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".heroes$ "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"dbService"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }).$;"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"html","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"html","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" *ngFor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"let hero of heroes$ | async"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {{ hero.name }}"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]})]})})}),"\n",(0,i.jsx)(s.h3,{id:"with-angular-signals",children:"With Angular Signals"}),"\n",(0,i.jsxs)(s.p,{children:["Angular Signals are a newer approach for reactivity. RxDB supports them via a ",(0,i.jsx)(s.a,{href:"/reactivity.html",children:"custom reactivity"})," factory. You can convert RxJS Observables to Signals using Angular's ",(0,i.jsx)(s.code,{children:"toSignal"}),":"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxReactivityFactory } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" untracked"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Injector } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@angular/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { toSignal } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@angular/core/rxjs-interop'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createReactivityFactory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(injector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Injector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" RxReactivityFactory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"Signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">> {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fromObservable"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(observable$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" initialValue) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" untracked"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" toSignal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(observable$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" initialValue"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" injector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" rejectErrors"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Pass this factory when creating your ",(0,i.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"}),":"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { inject"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Injector } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@angular/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" reactivity"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createReactivityFactory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"inject"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(Injector))"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Use the double-dollar sign (",(0,i.jsx)(s.code,{children:"$$"}),") to get a ",(0,i.jsx)(s.code,{children:"Signal"})," instead of an ",(0,i.jsx)(s.code,{children:"Observable"}),":"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroesSignal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$;"})]})})})}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"html","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"html","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" *ngFor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"let hero of heroesSignal()"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {{ hero.name }}"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"angular-indexeddb-example-with-rxdb",children:"Angular IndexedDB Example with RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["A comprehensive example of RxDB in an Angular application is available in the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/angular",children:"RxDB GitHub repository"}),". It demonstrates ",(0,i.jsx)(s.a,{href:"/articles/angular-database.html",children:"database"})," creation, queries, and Angular integration using best practices."]}),"\n",(0,i.jsx)(s.h2,{id:"advanced-rxdb-features",children:"Advanced RxDB Features"}),"\n",(0,i.jsx)(s.p,{children:"Beyond simple CRUD and local data storage, RxDB supports:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Replication"}),": Sync your local data with a remote database. Learn more at ",(0,i.jsx)(s.a,{href:"https://rxdb.info/replication.html",children:"RxDB Replication"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Data Migration on Schema Changes"}),": RxDB supports automatic or manual schema migrations to manage backward-compatibility and evolve your data structure. See ",(0,i.jsx)(s.a,{href:"https://rxdb.info/migration-schema.html",children:"RxDB Migration"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Encryption"}),": Easily encrypt sensitive data at rest. See ",(0,i.jsx)(s.a,{href:"https://rxdb.info/encryption.html",children:"RxDB Encryption"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Compression"}),": Reduce storage and bandwidth usage using key compression. Learn more at ",(0,i.jsx)(s.a,{href:"https://rxdb.info/key-compression.html",children:"RxDB Key Compression"}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"limitations-of-indexeddb",children:"Limitations of IndexedDB"}),"\n",(0,i.jsx)(s.p,{children:"While IndexedDB works well for many use cases, it does have a few constraints:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Potentially Slow"}),": While adequate for most use cases, IndexedDB performance can degrade for very large datasets. More details at RxDB ",(0,i.jsx)(s.a,{href:"/slow-indexeddb.html",children:"Slow IndexedDB"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Storage Limits"}),": Browsers may cap the amount of data you can store in IndexedDB. For more info, see ",(0,i.jsx)(s.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"Local Storage Limits of IndexedDB"}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"alternatives-to-indexeddb",children:"Alternatives to IndexedDB"}),"\n",(0,i.jsx)(s.p,{children:"Depending on your needs, you might explore:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Origin Private File System (OPFS)"}),": A newer browser storage mechanism that can offer better performance. RxDB supports ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS storage"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"SQLite"}),": When building a mobile or hybrid app (e.g., with ",(0,i.jsx)(s.a,{href:"/capacitor-database.html",children:"Capacitor"})," or ",(0,i.jsx)(s.a,{href:"/articles/ionic-database.html",children:"Ionic"}),"), you can use SQLite locally. See ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"RxDB with SQLite"}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"performance-comparison-with-other-browser-storages",children:"Performance comparison with other browser storages"}),"\n",(0,i.jsxs)(s.p,{children:["Here is a ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance overview"})," of the various browser based storage implementation of RxDB:"]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"700"})}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsx)(s.p,{children:"Continue your deep dive into RxDB with official quickstart guides and star the repository on GitHub to stay updated."}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"RxDB Quickstart"}),": Get started quickly with the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"RxDB GitHub"}),": Explore the source, open issues, and star \u2b50 the project at ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB GitHub Repo"}),"."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"By combining IndexedDB's local storage with RxDB's powerful features, you can build performant, robust, and offline-capable Angular applications. RxDB takes care of the lower-level complexities, letting you focus on delivering a great user experience-online or off."})]})}function p(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/6187b59a.746cfe2d.js b/docs/assets/js/6187b59a.746cfe2d.js
deleted file mode 100644
index 6c0aee00d1a..00000000000
--- a/docs/assets/js/6187b59a.746cfe2d.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5866],{5640(e,s,r){r.r(s),r.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"rx-storage-worker","title":"Turbocharge RxDB with Worker RxStorage","description":"Offload RxDB queries to WebWorkers or Worker Threads, freeing the main thread and boosting performance. Experience smoother apps with Worker RxStorage.","source":"@site/docs/rx-storage-worker.md","sourceDirName":".","slug":"/rx-storage-worker.html","permalink":"/rx-storage-worker.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Turbocharge RxDB with Worker RxStorage","slug":"rx-storage-worker.html","description":"Offload RxDB queries to WebWorkers or Worker Threads, freeing the main thread and boosting performance. Experience smoother apps with Worker RxStorage."},"sidebar":"tutorialSidebar","previous":{"title":"Remote RxStorage","permalink":"/rx-storage-remote.html"},"next":{"title":"SharedWorker RxStorage \ud83d\udc51","permalink":"/rx-storage-shared-worker.html"}}');var o=r(4848),i=r(8453);const a={title:"Turbocharge RxDB with Worker RxStorage",slug:"rx-storage-worker.html",description:"Offload RxDB queries to WebWorkers or Worker Threads, freeing the main thread and boosting performance. Experience smoother apps with Worker RxStorage."},l="Worker RxStorage",t={},c=[{value:"On the worker process",id:"on-the-worker-process",level:2},{value:"On the main process",id:"on-the-main-process",level:2},{value:"Pre-build workers",id:"pre-build-workers",level:2},{value:"Building a custom worker",id:"building-a-custom-worker",level:2},{value:"One worker per database",id:"one-worker-per-database",level:2},{value:"Passing in a Worker instance",id:"passing-in-a-worker-instance",level:2}];function d(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"worker-rxstorage",children:"Worker RxStorage"})}),"\n",(0,o.jsxs)(s.p,{children:["With the worker plugin, you can put the ",(0,o.jsx)(s.code,{children:"RxStorage"})," of your database inside of a WebWorker (in browsers) or a Worker Thread (in node.js). By doing so, you can take CPU load from the main process and move it into the worker's process which can improve the perceived performance of your application. Notice that for browsers, it is recommend to use the ",(0,o.jsx)(s.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker"})," instead to get a better performance."]}),"\n",(0,o.jsx)(s.admonition,{title:"Premium",type:"note",children:(0,o.jsxs)(s.p,{children:["This plugin is part of ",(0,o.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"}),". It is not part of the default RxDB module."]})}),"\n",(0,o.jsx)(s.h2,{id:"on-the-worker-process",children:"On the worker process"}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// worker.ts"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { exposeWorkerRxStorage } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"exposeWorkerRxStorage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can wrap any implementation of the RxStorage interface"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * into a worker."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here we use the IndexedDB RxStorage."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"on-the-main-process",children:"On the main process"}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageWorker } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageWorker"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Contains any value that can be used as parameter"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * to the Worker constructor of thread.js"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Most likely you want to put the path to the worker.js file in here."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'path/to/worker.js'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * (Optional) options"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * for the worker."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workerOptions"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'module'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" credentials"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'omit'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"pre-build-workers",children:"Pre-build workers"}),"\n",(0,o.jsxs)(s.p,{children:["The ",(0,o.jsx)(s.code,{children:"worker.js"})," must be a self containing JavaScript file that contains all dependencies in a bundle.\nTo make it easier for you, RxDB ships with pre-bundles worker files that are ready to use.\nYou can find them in the folder ",(0,o.jsx)(s.code,{children:"node_modules/rxdb-premium/dist/workers"})," after you have installed the ",(0,o.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51 Plugin"}),". From there you can copy them to a location where it can be served from the webserver and then use their path to create the ",(0,o.jsx)(s.code,{children:"RxDatabase"}),"."]}),"\n",(0,o.jsxs)(s.p,{children:["Any valid ",(0,o.jsx)(s.code,{children:"worker.js"})," JavaScript file can be used both, for normal Workers and SharedWorkers."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageWorker } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageWorker"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Path to where the copied file from node_modules/rxdb/dist/workers"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * is reachable from the webserver."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/indexeddb.worker.js'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"building-a-custom-worker",children:"Building a custom worker"}),"\n",(0,o.jsxs)(s.p,{children:["The easiest way to bundle a custom ",(0,o.jsx)(s.code,{children:"worker.js"})," file is by using webpack. Here is the webpack-config that is also used for the prebuild workers:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// webpack.config.js"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" path"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'path'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" TerserPlugin"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'terser-webpack-plugin'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" projectRootPath"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" path"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".resolve"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" __dirname"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '../../'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // path from webpack-config to the root folder of the repo"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" babelConfig"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"path"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".join"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(projectRootPath"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'babel.config'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" baseDir"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" './dist/workers/'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"; "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// output path"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"module"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"exports"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" target"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'webworker'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" entry"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-custom-worker'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" baseDir "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-custom-worker.js'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" output"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" filename"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '[name].js'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" clean"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" path"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" path"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".resolve"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" projectRootPath"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'dist/workers'"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mode"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'production'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" module"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" rules"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" test"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" /\\.tsx"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"?$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"/"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" exclude"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" /(node_modules)/"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" use"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" loader"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'babel-loader'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" options"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" babelConfig"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" resolve"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" extensions"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'.tsx'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '.ts'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '.js'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '.mjs'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '.mts'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" optimization"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" moduleIds"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'deterministic'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" minimize"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" minimizer"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" TerserPlugin"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" terserOptions"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" format"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" comments"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" extractComments"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })]"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"one-worker-per-database",children:"One worker per database"}),"\n",(0,o.jsxs)(s.p,{children:["Each call to ",(0,o.jsx)(s.code,{children:"getRxStorageWorker()"})," will create a different worker instance so that when you have more than one ",(0,o.jsx)(s.code,{children:"RxDatabase"}),", each database will have its own JavaScript worker process."]}),"\n",(0,o.jsxs)(s.p,{children:["To reuse the worker instance in more than one ",(0,o.jsx)(s.code,{children:"RxDatabase"}),", you can store the output of ",(0,o.jsx)(s.code,{children:"getRxStorageWorker()"})," into a variable an use that one. Reusing the worker can decrease the initial page load, but you might get slower database operations."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Call getRxStorageWorker() exactly once"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" workerStorage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageWorker"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'path/to/worker.js'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// use the same storage for both databases."})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" databaseOne"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'database-one'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workerStorage"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" databaseTwo"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'database-two'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workerStorage"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "})]})})}),"\n",(0,o.jsx)(s.h2,{id:"passing-in-a-worker-instance",children:"Passing in a Worker instance"}),"\n",(0,o.jsxs)(s.p,{children:["Instead of setting an url as ",(0,o.jsx)(s.code,{children:"workerInput"}),", you can also specify a function that returns a new ",(0,o.jsx)(s.code,{children:"Worker"})," instance when called."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"getRxStorageWorker"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" workerInput"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Worker"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'path/to/worker.js'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"})})]})})}),"\n",(0,o.jsxs)(s.p,{children:["This can be helpful for environments where the worker is build dynamically by the bundler. For example in angular you would create a ",(0,o.jsx)(s.code,{children:"my-custom.worker.ts"})," file that contains a custom build worker and then import it."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageWorker"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" workerInput"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Worker"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" URL"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'./my-custom.worker'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"meta"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".url))"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> my-custom.worker.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { exposeWorkerRxStorage } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"exposeWorkerRxStorage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]})}function h(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>l});var n=r(6540);const o={},i=n.createContext(o);function a(e){const s=n.useContext(i);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),n.createElement(i.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/6672.9c7472fc.js b/docs/assets/js/6672.9c7472fc.js
deleted file mode 100644
index a4dd750bc95..00000000000
--- a/docs/assets/js/6672.9c7472fc.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4291,6672],{4291(e,t,r){r.r(t),r.d(t,{getDatabase:()=>wa,hasIndexedDB:()=>ba});var a=r(7329);function n(e,t){var r=e.get(t);if(void 0===r)throw new Error("missing value from map "+t);return r}function i(e,t,r,a){var n=e.get(t);return void 0===n?(n=r(),e.set(t,n)):a&&a(n),n}function s(e){return Object.assign({},e)}function o(e,t=!1){if(!e)return e;if(!t&&Array.isArray(e))return e.sort((e,t)=>"string"==typeof e&&"string"==typeof t?e.localeCompare(t):"object"==typeof e?1:-1).map(e=>o(e,t));if("object"==typeof e&&!Array.isArray(e)){var r={};return Object.keys(e).sort((e,t)=>e.localeCompare(t)).forEach(a=>{r[a]=o(e[a],t)}),r}return e}var c=function e(t){if(!t)return t;if(null===t||"object"!=typeof t)return t;if(Array.isArray(t)){for(var r=new Array(t.length),a=r.length;a--;)r[a]=e(t[a]);return r}var n={};for(var i in t)n[i]=e(t[i]);return n};function u(e,t,r){return Object.defineProperty(e,t,{get:function(){return r}}),r}var h=e=>{var t=typeof e;return null!==e&&("object"===t||"function"===t)},l=new Set(["__proto__","prototype","constructor"]),d=new Set("0123456789");function m(e){var t=[],r="",a="start",n=!1;for(var i of e)switch(i){case"\\":if("index"===a)throw new Error("Invalid character in an index");if("indexEnd"===a)throw new Error("Invalid character after an index");n&&(r+=i),a="property",n=!n;break;case".":if("index"===a)throw new Error("Invalid character in an index");if("indexEnd"===a){a="property";break}if(n){n=!1,r+=i;break}if(l.has(r))return[];t.push(r),r="",a="property";break;case"[":if("index"===a)throw new Error("Invalid character in an index");if("indexEnd"===a){a="index";break}if(n){n=!1,r+=i;break}if("property"===a){if(l.has(r))return[];t.push(r),r=""}a="index";break;case"]":if("index"===a){t.push(Number.parseInt(r,10)),r="",a="indexEnd";break}if("indexEnd"===a)throw new Error("Invalid character after an index");default:if("index"===a&&!d.has(i))throw new Error("Invalid character in an index");if("indexEnd"===a)throw new Error("Invalid character after an index");"start"===a&&(a="property"),n&&(n=!1,r+="\\"),r+=i}switch(n&&(r+="\\"),a){case"property":if(l.has(r))return[];t.push(r);break;case"index":throw new Error("Index was not closed");case"start":t.push("")}return t}function f(e,t){if("number"!=typeof t&&Array.isArray(e)){var r=Number.parseInt(t,10);return Number.isInteger(r)&&e[r]===e[t]}return!1}function p(e,t){if(f(e,t))throw new Error("Cannot use string index")}function v(e,t,r){if(Array.isArray(t)&&(t=t.join(".")),!t.includes(".")&&!t.includes("["))return e[t];if(!h(e)||"string"!=typeof t)return void 0===r?e:r;var a=m(t);if(0===a.length)return r;for(var n=0;n!1,deepFreezeWhenDevMode:e=>e,tunnelErrorMessage:e=>"\n RxDB Error-Code: "+e+".\n Hint: Error messages are not included in RxDB core to reduce build size.\n To show the full error messages and to ensure that you do not make any mistakes when using RxDB,\n use the dev-mode plugin when you are in development mode: https://rxdb.info/dev-mode.html?console=error\n "};function _(e,t,r){return"\n"+e+"\n"+function(e){var t="";return 0===Object.keys(e).length?t:(t+="-".repeat(20)+"\n",t+="Parameters:\n",t+=Object.keys(e).map(t=>{var r="[object Object]";try{r="errors"===t?e[t].map(e=>JSON.stringify(e,Object.getOwnPropertyNames(e))):JSON.stringify(e[t],function(e,t){return void 0===t?null:t},2)}catch(a){}return t+": "+r}).join("\n"),t+="\n")}(r)}var k=function(e){function t(t,r,a={}){var n,i=_(r,0,a);return(n=e.call(this,i)||this).code=t,n.message=i,n.url=E(t),n.parameters=a,n.rxdb=!0,n}return(0,w.A)(t,e),t.prototype.toString=function(){return this.message},(0,b.A)(t,[{key:"name",get:function(){return"RxError ("+this.code+")"}},{key:"typeError",get:function(){return!1}}])}((0,D.A)(Error)),I=function(e){function t(t,r,a={}){var n,i=_(r,0,a);return(n=e.call(this,i)||this).code=t,n.message=i,n.url=E(t),n.parameters=a,n.rxdb=!0,n}return(0,w.A)(t,e),t.prototype.toString=function(){return this.message},(0,b.A)(t,[{key:"name",get:function(){return"RxTypeError ("+this.code+")"}},{key:"typeError",get:function(){return!0}}])}((0,D.A)(TypeError));function E(e){return"https://rxdb.info/errors.html?console=errors#"+e}function C(e){return"\nFind out more about this error here: "+E(e)+" \n"}function R(e,t){return new k(e,x.tunnelErrorMessage(e)+C(e),t)}function O(e,t){return new I(e,x.tunnelErrorMessage(e)+C(e),t)}function S(e){return!(!e||409!==e.status)&&e}var j={409:"document write conflict",422:"schema validation error",510:"attachment data missing"};function P(e){return R("COL20",{name:j[e.status],document:e.documentId,writeError:e})}var $=/\./g,N=r(8342);function B(e,t){var r=t;return r="properties."+(r=r.replace($,".properties.")),v(e,r=(0,N.L$)(r))}function M(e){return"string"==typeof e?e:e.key}function A(e,t){if("string"==typeof e.primaryKey)return t[e.primaryKey];var r=e.primaryKey;return r.fields.map(e=>{var r=v(t,e);if(void 0===r)throw R("DOC18",{args:{field:e,documentData:t}});return r}).join(r.separator)}function q(e){var t=M((e=s(e)).primaryKey);e.properties=s(e.properties),e.additionalProperties=!1,Object.prototype.hasOwnProperty.call(e,"keyCompression")||(e.keyCompression=!1),e.indexes=e.indexes?e.indexes.slice(0):[],e.required=e.required?e.required.slice(0):[],e.encrypted=e.encrypted?e.encrypted.slice(0):[],e.properties._rev={type:"string",minLength:1},e.properties._attachments={type:"object"},e.properties._deleted={type:"boolean"},e.properties._meta=T,e.required=e.required?e.required.slice(0):[],e.required.push("_deleted"),e.required.push("_rev"),e.required.push("_meta"),e.required.push("_attachments"),e.required.push(t),e.required=e.required.filter(e=>!e.includes(".")).filter((e,t,r)=>r.indexOf(e)===t),e.version=e.version||0;var r=e.indexes.map(e=>{var r=(0,g.k_)(e)?e.slice(0):[e];return r.includes(t)||r.push(t),"_deleted"!==r[0]&&r.unshift("_deleted"),r});0===r.length&&r.push(function(e){return["_deleted",e]}(t)),r.push(["_meta.lwt",t]),e.internalIndexes&&e.internalIndexes.map(e=>{r.push(e)});var a=new Set;return r.filter(e=>{var t=e.join(",");return!a.has(t)&&(a.add(t),!0)}),e.indexes=r,e}var T={type:"object",properties:{lwt:{type:"number",minimum:1,maximum:1e15,multipleOf:.01}},additionalProperties:!0,required:["lwt"]};var L="docs",Q="changes",W="attachments",F="dexie",H=new Map,K=new Map;var z="__";function Z(e){var t=e.split(".");if(t.length>1)return t.map(e=>Z(e)).join(".");if(e.startsWith("|")){var r=e.substring(1);return z+r}return e}function U(e){var t=e.split(".");return t.length>1?t.map(e=>U(e)).join("."):e.startsWith(z)?"|"+e.substring(2):e}function V(e,t){if(!t)return t;var r=s(t);return r=G(r),e.forEach(e=>{var a=v(t,e)?"1":"0",n=Z(e);y(r,n,a)}),r}function J(e,t){return t?(t=X(t=s(t)),e.forEach(e=>{var r=v(t,e);y(t,e,"1"===r)}),t):t}function G(e){if(!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e)return e;if(Array.isArray(e))return e.map(e=>G(e));if("object"==typeof e){var t={};return Object.entries(e).forEach(([e,r])=>{"object"==typeof r&&(r=G(r)),t[Z(e)]=r}),t}}function X(e){if(!e||"string"==typeof e||"number"==typeof e||"boolean"==typeof e)return e;if(Array.isArray(e))return e.map(e=>X(e));if("object"==typeof e){var t={};return Object.entries(e).forEach(([r,a])=>{("object"==typeof a||Array.isArray(e))&&(a=X(a)),t[U(r)]=a}),t}}function Y(e){var t=[],r=M(e.primaryKey);t.push([r]),t.push(["_deleted",r]),e.indexes&&e.indexes.forEach(e=>{var r=(0,g.$r)(e);t.push(r)}),t.push(["_meta.lwt",r]),t.push(["_meta.lwt"]);var a=(t=t.map(e=>e.map(e=>Z(e)))).map(e=>1===e.length?e[0]:"["+e.join("+")+"]");return(a=a.filter((e,t,r)=>r.indexOf(e)===t)).join(", ")}async function ee(e,t){var r=await e;return(await r.dexieTable.bulkGet(t)).map(e=>J(r.booleanIndexes,e))}function te(e,t){return e+"||"+t}function re(e){var t=new Set,r=[];return e.indexes?(e.indexes.forEach(a=>{(0,g.$r)(a).forEach(a=>{t.has(a)||(t.add(a),"boolean"===B(e,a).type&&r.push(a))})}),r.push("_deleted"),(0,g.jw)(r)):r}var ae=r(9092),ne=0;function ie(){var e=Date.now();(e+=.01)<=ne&&(e=ne+.01);var t=parseFloat(e.toFixed(2));return ne=t,t}var se,oe={};var ce=async function(e){var t=(new TextEncoder).encode(e),r=await function(){if(se)return se;if("undefined"==typeof crypto||void 0===crypto.subtle||"function"!=typeof crypto.subtle.digest)throw R("UT8",{args:{typeof_crypto:typeof crypto,typeof_crypto_subtle:typeof crypto?.subtle,typeof_crypto_subtle_digest:typeof crypto?.subtle?.digest}});return se=crypto.subtle.digest.bind(crypto.subtle)}()("SHA-256",t);return Array.prototype.map.call(new Uint8Array(r),e=>("00"+e.toString(16)).slice(-2)).join("")};var ue=r(4134),he=ue.Dr,le=!1;async function de(){return le?he:(le=!0,he=(async()=>!(!oe.premium||"string"!=typeof oe.premium||"6da4936d1425ff3a5c44c02342c6daf791d266be3ae8479b8ec59e261df41b93"!==await ce(oe.premium)))())}var me=r(3337),fe=String.fromCharCode(65535),pe=Number.MIN_SAFE_INTEGER;function ve(e,t){var r=t.selector,a=e.indexes?e.indexes.slice(0):[];t.index&&(a=[t.index]);var n=!!t.sort.find(e=>"desc"===Object.values(e)[0]),i=new Set;Object.keys(r).forEach(t=>{var a=B(e,t);a&&"boolean"===a.type&&Object.prototype.hasOwnProperty.call(r[t],"$eq")&&i.add(t)});var s,o=t.sort.map(e=>Object.keys(e)[0]).filter(e=>!i.has(e)).join(","),c=-1;if(a.forEach(e=>{var a=!0,u=!0,h=e.map(e=>{var t=r[e],n=t?Object.keys(t):[],i={};t&&n.length?n.forEach(e=>{if(ye.has(e)){var r=function(e,t){switch(e){case"$eq":return{startKey:t,endKey:t,inclusiveEnd:!0,inclusiveStart:!0};case"$lte":return{endKey:t,inclusiveEnd:!0};case"$gte":return{startKey:t,inclusiveStart:!0};case"$lt":return{endKey:t,inclusiveEnd:!1};case"$gt":return{startKey:t,inclusiveStart:!1};default:throw new Error("SNH")}}(e,t[e]);i=Object.assign(i,r)}}):i={startKey:u?pe:fe,endKey:a?fe:pe,inclusiveStart:!0,inclusiveEnd:!0};return void 0===i.startKey&&(i.startKey=pe),void 0===i.endKey&&(i.endKey=fe),void 0===i.inclusiveStart&&(i.inclusiveStart=!0),void 0===i.inclusiveEnd&&(i.inclusiveEnd=!0),u&&!i.inclusiveStart&&(u=!1),a&&!i.inclusiveEnd&&(a=!1),i}),l=h.map(e=>e.startKey),d=h.map(e=>e.endKey),m={index:e,startKeys:l,endKeys:d,inclusiveEnd:a,inclusiveStart:u,sortSatisfiedByIndex:!n&&o===e.filter(e=>!i.has(e)).join(","),selectorSatisfiedByIndex:we(e,t.selector,l,d)},f=function(e,t,r){var a=0,n=e=>{e>0&&(a+=e)},i=10,s=(0,g.Sd)(r.startKeys,e=>e!==pe&&e!==fe);n(s*i);var o=(0,g.Sd)(r.startKeys,e=>e!==fe&&e!==pe);n(o*i);var c=(0,g.Sd)(r.startKeys,(e,t)=>e===r.endKeys[t]);n(c*i*1.5);var u=r.sortSatisfiedByIndex?5:0;return n(u),a}(0,0,m);(f>=c||t.index)&&(c=f,s=m)}),!s)throw R("SNH",{query:t});return s}var ye=new Set(["$eq","$gt","$gte","$lt","$lte"]),ge=new Set(["$eq","$gt","$gte"]),be=new Set(["$eq","$lt","$lte"]);function we(e,t,r,a){var n=Object.entries(t).find(([t,r])=>!e.includes(t)||Object.entries(r).find(([e,t])=>!ye.has(e)));if(n)return!1;if(t.$and||t.$or)return!1;var i=[],s=new Set;for(var[o,c]of Object.entries(t)){if(!e.includes(o))return!1;var u=Object.keys(c).filter(e=>ge.has(e));if(u.length>1)return!1;var h=u[0];if(h&&s.add(o),"$eq"!==h){if(i.length>0)return!1;i.push(h)}}var l=[],d=new Set;for(var[m,f]of Object.entries(t)){if(!e.includes(m))return!1;var p=Object.keys(f).filter(e=>be.has(e));if(p.length>1)return!1;var v=p[0];if(v&&d.add(m),"$eq"!==v){if(l.length>0)return!1;l.push(v)}}var y=0;for(var g of e){for(var b of[s,d]){if(!b.has(g)&&b.size>0)return!1;b.delete(g)}if(r[y]!==a[y]&&s.size>0&&d.size>0)return!1;y++}return!0}var De,xe=r(2235),_e=r(4953),ke=r(1621),Ie=r(4192),Ee=r(695),Ce=r(2830),Re=r(2080),Oe=r(175),Se=r(8259),je=r(5912),Pe=r(6729),$e=r(6529),Ne=r(2226),Be=r(3697),Me=r(558),Ae=r(2583),qe=r(9283),Te=r(5629),Le=r(4612),Qe=r(5805),We=r(3587),Fe=r(1401),He=r(7531),Ke=!1;function ze(e){return Ke||(De=_e.ob.init({pipeline:{$sort:Ie.x,$project:Ee.C},query:{$elemMatch:Ce.J,$eq:Re.X,$nor:Oe.q,$exists:Se.P,$regex:je.W,$and:Pe.a,$gt:$e.M,$gte:Ne.f,$in:Be.o,$lt:Me.N,$lte:Ae.Q,$ne:qe.C,$nin:Te.G,$mod:Le.P,$not:Qe.E,$or:We.s,$size:Fe.I,$type:He.T}}),Ke=!0),new ke.X(e,{context:De})}function Ze(e,t){var r=M(e.primaryKey);t=s(t);var a=c(t);if("number"!=typeof a.skip&&(a.skip=0),a.selector?(a.selector=a.selector,Object.entries(a.selector).forEach(([e,t])=>{"object"==typeof t&&null!==t||(a.selector[e]={$eq:t})})):a.selector={},a.index){var n=(0,g.$r)(a.index);n.includes(r)||n.push(r),a.index=n}if(a.sort)a.sort.find(e=>{return t=e,Object.keys(t)[0]===r;var t})||(a.sort=a.sort.slice(0),a.sort.push({[r]:"asc"}));else if(a.index)a.sort=a.index.map(e=>({[e]:"asc"}));else{if(e.indexes){var i=new Set;Object.entries(a.selector).forEach(([e,t])=>{("object"!=typeof t||null===t||!!Object.keys(t).find(e=>ye.has(e)))&&i.add(e)});var o,u=-1;e.indexes.forEach(e=>{var t=(0,g.k_)(e)?e:[e],r=t.findIndex(e=>!i.has(e));r>0&&r>u&&(u=r,o=t)}),o&&(a.sort=o.map(e=>({[e]:"asc"})))}if(!a.sort)if(e.indexes&&e.indexes.length>0){var h=e.indexes[0],l=(0,g.k_)(h)?h:[h];a.sort=l.map(e=>({[e]:"asc"}))}else a.sort=[{[r]:"asc"}]}return a}function Ue(e,t){if(!t.sort)throw R("SNH",{query:t});var r=[];t.sort.forEach(e=>{var t,a,n,i=Object.keys(e)[0],s=Object.values(e)[0];r.push({key:i,direction:s,getValueFn:(t=i,a=t.split("."),n=a.length,1===n?e=>e[t]:e=>{for(var t=e,r=0;r{for(var a=0;ar.test(e)}async function Je(e,t){var r=await e.exec();return r?Array.isArray(r)?Promise.all(r.map(e=>t(e))):r instanceof Map?Promise.all([...r.values()].map(e=>t(e))):await t(r):null}function Ge(e,t){if(!t.sort)throw R("SNH",{query:t});return{query:t,queryPlan:ve(e,t)}}function Xe(e){return e===pe?-1/0:e}function Ye(e,t,r){return e.includes(t)?r===fe||!0===r?"1":"0":r}function et(e,t,r){if(!r){if("undefined"==typeof window)throw new Error("IDBKeyRange missing");r=window.IDBKeyRange}var a=t.startKeys.map((r,a)=>{var n=t.index[a];return Ye(e,n,r)}).map(Xe),n=t.endKeys.map((r,a)=>{var n=t.index[a];return Ye(e,n,r)}).map(Xe);return r.bound(a,n,!t.inclusiveStart,!t.inclusiveEnd)}async function tt(e,t){var r=await e.internals,a=t.query,n=a.skip?a.skip:0,i=n+(a.limit?a.limit:1/0),s=t.queryPlan,o=!1;s.selectorSatisfiedByIndex||(o=Ve(e.schema,t.query));var c=et(r.booleanIndexes,s,r.dexieDb._options.IDBKeyRange),u=s.index,h=[];if(await r.dexieDb.transaction("r",r.dexieTable,async e=>{var t,a=e.idbtrans.objectStore(L);t="["+u.map(e=>Z(e)).join("+")+"]";var n=a.index(t).openCursor(c);await new Promise(e=>{n.onsuccess=function(t){var a=t.target.result;if(a){var n=J(r.booleanIndexes,a.value);o&&!o(n)||h.push(n),s.sortSatisfiedByIndex&&h.length===i?e():a.continue()}else e()}})}),!s.sortSatisfiedByIndex){var l=Ue(e.schema,t.query);h=h.sort(l)}return{documents:h=h.slice(n,i)}}function rt(e){for(var t="",r=0;r0&&nt[e].forEach(e=>e(t))}async function st(e,t){for(var r of nt[e])await r(t)}async function ot(e,t){var r=(await e.findDocumentsById([t],!1))[0];return r||void 0}async function ct(e,t,r){var a=await e.bulkWrite([t],r);if(a.error.length>0)throw a.error[0];return vt(M(e.schema.primaryKey),[t],a)[0]}function ut(e,t,r,a){if(a)throw 409===a.status?R("CONFLICT",{collection:e.name,id:t,writeError:a,data:r}):422===a.status?R("VD2",{collection:e.name,id:t,writeError:a,data:r}):a}function ht(e){return{previous:e.previous,document:lt(e.document)}}function lt(e){if(!e._attachments||0===Object.keys(e._attachments).length)return e;var t=s(e);return t._attachments={},Object.entries(e._attachments).forEach(([e,r])=>{var a,n,i;t._attachments[e]=(i=(a=r).data)?{length:(n=i,atob(n).length),digest:a.digest,type:a.type}:a}),t}function dt(e){return Object.assign({},e,{_meta:s(e._meta)})}function mt(e,t,r){x.deepFreezeWhenDevMode(r);var a=M(t.schema.primaryKey),n={originalStorageInstance:t,schema:t.schema,internals:t.internals,collectionName:t.collectionName,databaseName:t.databaseName,options:t.options,async bulkWrite(r,n){for(var i=e.token,s=new Array(r.length),o=ie(),c=0;ct.bulkWrite(s,n)),m={error:[]};ft.set(m,s);var f=0===d.error.length?[]:d.error.filter(e=>!(409!==e.status||e.writeRow.previous||e.writeRow.document._deleted||!(0,me.ZN)(e.documentInDb)._deleted)||(m.error.push(e),!1));if(f.length>0){var p=new Set,v=f.map(t=>(p.add(t.documentId),{previous:t.documentInDb,document:Object.assign({},t.writeRow.document,{_rev:at(e.token,t.documentInDb)})})),y=await e.lockedRun(()=>t.bulkWrite(v,n));m.error=m.error.concat(y.error);var g=vt(a,s,m,p),b=vt(a,v,y);return g.push(...b),m}return m},query:r=>e.lockedRun(()=>t.query(r)),count:r=>e.lockedRun(()=>t.count(r)),findDocumentsById:(r,a)=>e.lockedRun(()=>t.findDocumentsById(r,a)),getAttachmentData:(r,a,n)=>e.lockedRun(()=>t.getAttachmentData(r,a,n)),getChangedDocumentsSince:t.getChangedDocumentsSince?(r,a)=>e.lockedRun(()=>t.getChangedDocumentsSince((0,me.ZN)(r),a)):void 0,cleanup:r=>e.lockedRun(()=>t.cleanup(r)),remove:()=>(e.storageInstances.delete(n),e.lockedRun(()=>t.remove())),close:()=>(e.storageInstances.delete(n),e.lockedRun(()=>t.close())),changeStream:()=>t.changeStream()};return e.storageInstances.add(n),n}var ft=new WeakMap,pt=new WeakMap;function vt(e,t,r,a){return i(pt,r,()=>{var n=[],i=ft.get(r);if(i||(i=t),r.error.length>0||a){for(var s=a||new Set,o=0;o{r.storageName===e&&r.databaseName===t.databaseName&&r.collectionName===t.collectionName&&r.version===t.schema.version&&i.next(r.eventBulk)};n.addEventListener("message",s);var o=r.changeStream(),c=!1,u=o.subscribe(r=>{c||n.postMessage({storageName:e,databaseName:t.databaseName,collectionName:t.collectionName,version:t.schema.version,eventBulk:r})});r.changeStream=function(){return i.asObservable().pipe((0,yt.X)(o))};var h=r.close.bind(r);r.close=async function(){return c=!0,u.unsubscribe(),n.removeEventListener("message",s),a||await wt(t.databaseInstanceToken,r),h()};var l=r.remove.bind(r);r.remove=async function(){return c=!0,u.unsubscribe(),n.removeEventListener("message",s),a||await wt(t.databaseInstanceToken,r),l()}}}var xt=ie(),_t=!1,kt=function(){function e(e,t,r,a,n,i,s,o){this.changes$=new ae.B,this.instanceId=xt++,this.storage=e,this.databaseName=t,this.collectionName=r,this.schema=a,this.internals=n,this.options=i,this.settings=s,this.devMode=o,this.primaryPath=M(this.schema.primaryKey)}var t=e.prototype;return t.bulkWrite=async function(e,t){Et(this),_t||await de()||console.warn(["-------------- RxDB Open Core RxStorage -------------------------------","You are using the free Dexie.js based RxStorage implementation from RxDB https://rxdb.info/rx-storage-dexie.html?console=dexie ","While this is a great option, we want to let you know that there are faster storage solutions available in our premium plugins.","For professional users and production environments, we highly recommend considering these premium options to enhance performance and reliability."," https://rxdb.info/premium/?console=dexie ","If you already purchased premium access you can disable this log by calling the setPremiumFlag() function from rxdb-premium/plugins/shared.","---------------------------------------------------------------------"].join("\n")),_t=!0,e.forEach(e=>{if(!e.document._rev||e.previous&&!e.previous._rev)throw R("SNH",{args:{row:e}})});var r=await this.internals,a={error:[]};this.devMode&&(e=e.map(e=>{var t=dt(e.document);return{previous:e.previous,document:t}}));var n,i=e.map(e=>e.document[this.primaryPath]);if(await r.dexieDb.transaction("rw",r.dexieTable,r.dexieAttachmentsTable,async()=>{var s=new Map;(await ee(this.internals,i)).forEach(e=>{var t=e;return t&&s.set(t[this.primaryPath],t),t}),n=function(e,t,r,a,n,i,s){for(var o,c=!!e.schema.attachments,u=[],h=[],l=[],d={id:(0,N.zs)(10),events:[],checkpoint:null,context:n},m=d.events,f=[],p=[],v=[],y=r.size>0,g=a.length,b=function(){var e,d=a[w],g=d.document,b=d.previous,D=g[t],x=g._deleted,_=b&&b._deleted,k=void 0;if(y&&(k=r.get(D)),k){var I=k._rev;if(!b||b&&I!==b._rev){var E={isError:!0,status:409,documentId:D,writeRow:d,documentInDb:k,context:n};return l.push(E),1}var C=c?ht(d):d;c&&(x?b&&Object.keys(b._attachments).forEach(e=>{p.push({documentId:D,attachmentId:e,digest:(0,me.ZN)(b)._attachments[e].digest})}):(Object.entries(g._attachments).find(([t,r])=>((b?b._attachments[t]:void 0)||r.data||(e={documentId:D,documentInDb:k,isError:!0,status:510,writeRow:d,attachmentId:t,context:n}),!0)),e||Object.entries(g._attachments).forEach(([e,t])=>{var r=b?b._attachments[e]:void 0;if(r){var a=C.document._attachments[e].digest;t.data&&r.digest!==a&&v.push({documentId:D,attachmentId:e,attachmentData:t,digest:t.digest})}else f.push({documentId:D,attachmentId:e,attachmentData:t,digest:t.digest})}))),e?l.push(e):(c?(h.push(ht(C)),s&&s(g)):(h.push(C),s&&s(g)),o=C);var O=null,S=null,j=null;if(_&&!x)j="INSERT",O=c?lt(g):g;else if(!b||_||x){if(!x)throw R("SNH",{args:{writeRow:d}});j="DELETE",O=(0,me.ZN)(g),S=b}else j="UPDATE",O=c?lt(g):g,S=b;var P={documentId:D,documentData:O,previousDocumentData:S,operation:j};m.push(P)}else{var $=!!x;if(c&&Object.entries(g._attachments).forEach(([t,r])=>{r.data?f.push({documentId:D,attachmentId:t,attachmentData:r,digest:r.digest}):(e={documentId:D,isError:!0,status:510,writeRow:d,attachmentId:t,context:n},l.push(e))}),e||(c?(u.push(ht(d)),i&&i(g)):(u.push(d),i&&i(g)),o=d),!$){var N={documentId:D,operation:"INSERT",documentData:c?lt(g):g,previousDocumentData:c&&b?lt(b):b};m.push(N)}}},w=0;w{o.push(e.document)}),n.bulkUpdateDocs.forEach(e=>{o.push(e.document)}),(o=o.map(e=>V(r.booleanIndexes,e))).length>0&&await r.dexieTable.bulkPut(o);var c=[];n.attachmentsAdd.forEach(e=>{c.push({id:te(e.documentId,e.attachmentId),data:e.attachmentData.data})}),n.attachmentsUpdate.forEach(e=>{c.push({id:te(e.documentId,e.attachmentId),data:e.attachmentData.data})}),await r.dexieAttachmentsTable.bulkPut(c),await r.dexieAttachmentsTable.bulkDelete(n.attachmentsRemove.map(e=>te(e.documentId,e.attachmentId)))}),(n=(0,me.ZN)(n)).eventBulk.events.length>0){var s=(0,me.ZN)(n.newestRow).document;n.eventBulk.checkpoint={id:s[this.primaryPath],lwt:s._meta.lwt},this.changes$.next(n.eventBulk)}return a},t.findDocumentsById=async function(e,t){Et(this);var r=await this.internals,a=[];return await r.dexieDb.transaction("r",r.dexieTable,async()=>{(await ee(this.internals,e)).forEach(e=>{!e||e._deleted&&!t||a.push(e)})}),a},t.query=function(e){return Et(this),tt(this,e)},t.count=async function(e){if(e.queryPlan.selectorSatisfiedByIndex){var t=await async function(e,t){var r=await e.internals,a=t.queryPlan,n=a.index,i=et(r.booleanIndexes,a,r.dexieDb._options.IDBKeyRange),s=-1;return await r.dexieDb.transaction("r",r.dexieTable,async e=>{var t,r=e.idbtrans.objectStore(L);t="["+n.map(e=>Z(e)).join("+")+"]";var a=r.index(t).count(i);s=await new Promise((e,t)=>{a.onsuccess=function(){e(a.result)},a.onerror=e=>t(e)})}),s}(this,e);return{count:t,mode:"fast"}}return{count:(await tt(this,e)).documents.length,mode:"slow"}},t.changeStream=function(){return Et(this),this.changes$.asObservable()},t.cleanup=async function(e){Et(this);var t=await this.internals;return await t.dexieDb.transaction("rw",t.dexieTable,async()=>{var r=ie()-e,a=await t.dexieTable.where("_meta.lwt").below(r).toArray(),n=[];a.forEach(e=>{"1"===e._deleted&&n.push(e[this.primaryPath])}),await t.dexieTable.bulkDelete(n)}),!0},t.getAttachmentData=async function(e,t,r){Et(this);var a=await this.internals,n=te(e,t);return await a.dexieDb.transaction("r",a.dexieAttachmentsTable,async()=>{var r=await a.dexieAttachmentsTable.get(n);if(r)return r.data;throw new Error("attachment missing documentId: "+e+" attachmentId: "+t)})},t.remove=async function(){Et(this);var e=await this.internals;return await e.dexieTable.clear(),this.close()},t.close=function(){return this.closed||(this.closed=(async()=>{this.changes$.complete(),await async function(e){var t=await e,r=K.get(e)-1;0===r?(t.dexieDb.close(),K.delete(e)):K.set(e,r)}(this.internals)})()),this.closed},e}();async function It(e,t,r){var n=function(e,t,r,n){var o="rxdb-dexie-"+e+"--"+n.version+"--"+t,c=i(H,o,()=>{var e=(async()=>{var e=s(r);e.autoOpen=!1;var t=new a.cf(o,e);r.onCreate&&await r.onCreate(t,o);var i={[L]:Y(n),[Q]:"++sequence, id",[W]:"id"};return t.version(1).stores(i),await t.open(),{dexieDb:t,dexieTable:t[L],dexieAttachmentsTable:t[W],booleanIndexes:re(n)}})();return H.set(o,c),K.set(c,0),e});return c}(t.databaseName,t.collectionName,r,t.schema),o=new kt(e,t.databaseName,t.collectionName,t.schema,n,t.options,r,t.devMode);return await Dt(F,t,o),Promise.resolve(o)}function Et(e){if(e.closed)throw new Error("RxStorageInstanceDexie is closed "+e.databaseName+"-"+e.collectionName)}var Ct="17.0.0-beta.6",Rt=function(){function e(e){this.name=F,this.rxdbVersion=Ct,this.settings=e}return e.prototype.createStorageInstance=function(e){(function(e){if(e.schema.keyCompression)throw R("UT5",{args:{params:e}});if((t=e.schema).encrypted&&t.encrypted.length>0||t.attachments&&t.attachments.encrypted)throw R("UT6",{args:{params:e}});var t;if(e.schema.attachments&&e.schema.attachments.compression)throw R("UT7",{args:{params:e}})}(e),e.schema.indexes)&&e.schema.indexes.flat().filter(e=>!e.includes(".")).forEach(t=>{if(!e.schema.required||!e.schema.required.includes(t))throw R("DXE1",{field:t,schema:e.schema})});return It(this,e,this.settings)},e}();function Ot(e={}){return new Rt(e)}var St=r(686),jt=function(){function e(e,t){if(this.jsonSchema=e,this.hashFunction=t,this.indexes=function(e){return(e.indexes||[]).map(e=>(0,g.k_)(e)?e:[e])}(this.jsonSchema),this.primaryPath=M(this.jsonSchema.primaryKey),!e.properties[this.primaryPath].maxLength)throw R("SC39",{schema:e});this.finalFields=function(e){var t=Object.keys(e.properties).filter(t=>e.properties[t].final),r=M(e.primaryKey);return t.push(r),"string"!=typeof e.primaryKey&&e.primaryKey.fields.forEach(e=>t.push(e)),t}(this.jsonSchema)}var t=e.prototype;return t.validateChange=function(e,t){this.finalFields.forEach(r=>{if(!(0,St.b)(e[r],t[r]))throw R("DOC9",{dataBefore:e,dataAfter:t,fieldName:r,schema:this.jsonSchema})})},t.getDocumentPrototype=function(){var e={},t=B(this.jsonSchema,"");return Object.keys(t).forEach(t=>{var r=t;e.__defineGetter__(t,function(){if(this.get&&"function"==typeof this.get)return this.get(r)}),Object.defineProperty(e,t+"$",{get:function(){return this.get$(r)},enumerable:!1,configurable:!1}),Object.defineProperty(e,t+"$$",{get:function(){return this.get$$(r)},enumerable:!1,configurable:!1}),Object.defineProperty(e,t+"_",{get:function(){return this.populate(r)},enumerable:!1,configurable:!1})}),u(this,"getDocumentPrototype",()=>e),e},t.getPrimaryOfDocumentData=function(e){return A(this.jsonSchema,e)},(0,b.A)(e,[{key:"version",get:function(){return this.jsonSchema.version}},{key:"defaultValues",get:function(){var e={};return Object.entries(this.jsonSchema.properties).filter(([,e])=>Object.prototype.hasOwnProperty.call(e,"default")).forEach(([t,r])=>e[t]=r.default),u(this,"defaultValues",e)}},{key:"hash",get:function(){return u(this,"hash",this.hashFunction(JSON.stringify(this.jsonSchema)))}}])}();function Pt(e,t,r=!0){r&&it("preCreateRxSchema",e);var a=q(e);a=function(e){return o(e,!0)}(a),x.deepFreezeWhenDevMode(a);var n=new jt(a,t);return it("createRxSchema",n),n}var $t=r(7708),Nt=r(8146),Bt=r(3356),Mt=r(5520),At=r(8609);function qt(e){var t=e.split("-"),r="RxDB";return t.forEach(e=>{r+=(0,N.Z2)(e)}),r+="Plugin",new Error("You are using a function which must be overwritten by a plugin.\n You should either prevent the usage of this function or add the plugin via:\n import { "+r+" } from 'rxdb/plugins/"+e+"';\n addRxPlugin("+r+");\n ")}function Tt(e){return e.documentData?e.documentData:e.previousDocumentData}function Lt(e){switch(e.operation){case"INSERT":return{operation:e.operation,id:e.documentId,doc:e.documentData,previous:null};case"UPDATE":return{operation:e.operation,id:e.documentId,doc:x.deepFreezeWhenDevMode(e.documentData),previous:e.previousDocumentData?e.previousDocumentData:"UNKNOWN"};case"DELETE":return{operation:e.operation,id:e.documentId,doc:null,previous:e.previousDocumentData}}}var Qt=new Map;function Wt(e){return i(Qt,e,()=>{for(var t=new Array(e.events.length),r=e.events,a=e.collectionName,n=e.isLocal,i=x.deepFreezeWhenDevMode,s=0;s[]);return new Promise((r,n)=>{var i={lastKnownDocumentState:e,modifier:t,resolve:r,reject:n};(0,me.ZN)(a).push(i),this.triggerRun()})},t.triggerRun=async function(){if(!0!==this.isRunning&&0!==this.queueByDocId.size){this.isRunning=!0;var e=[],t=this.queueByDocId;this.queueByDocId=new Map,await Promise.all(Array.from(t.entries()).map(async([t,r])=>{var a,n,i,s=(a=r.map(e=>e.lastKnownDocumentState),n=a[0],i=rt(n._rev),a.forEach(e=>{var t=rt(e._rev);t>i&&(n=e,i=t)}),n),o=s;for(var u of r)try{o=await u.modifier(c(o))}catch(h){u.reject(h),u.reject=()=>{},u.resolve=()=>{}}try{await this.preWrite(o,s)}catch(h){return void r.forEach(e=>e.reject(h))}e.push({previous:s,document:o})}));var r=e.length>0?await this.storageInstance.bulkWrite(e,"incremental-write"):{error:[]};return await Promise.all(vt(this.primaryPath,e,r).map(e=>{var r=e[this.primaryPath];this.postWrite(e),n(t,r).forEach(t=>t.resolve(e))})),r.error.forEach(e=>{var r=e.documentId,a=n(t,r),s=S(e);if(s){var o=i(this.queueByDocId,r,()=>[]);a.reverse().forEach(e=>{e.lastKnownDocumentState=(0,me.ZN)(s.documentInDb),(0,me.ZN)(o).unshift(e)})}else{var c=P(e);a.forEach(e=>e.reject(c))}}),this.isRunning=!1,this.triggerRun()}},e}();function Ht(e){return async t=>{var r=function(e){return Object.assign({},e,{_meta:void 0,_deleted:void 0,_rev:void 0})}(t);r._deleted=t._deleted;var a=await e(r),n=Object.assign({},a,{_meta:t._meta,_attachments:t._attachments,_rev:t._rev,_deleted:void 0!==a._deleted?a._deleted:t._deleted});return void 0===n._deleted&&(n._deleted=!1),n}}var Kt={get primaryPath(){if(this.isInstanceOfRxDocument)return this.collection.schema.primaryPath},get primary(){var e=this;if(e.isInstanceOfRxDocument)return e._data[e.primaryPath]},get revision(){if(this.isInstanceOfRxDocument)return this._data._rev},get deleted$(){if(this.isInstanceOfRxDocument)return this.$.pipe((0,$t.T)(e=>e._data._deleted))},get deleted$$(){var e=this;return e.collection.database.getReactivityFactory().fromObservable(e.deleted$,e.getLatest().deleted,e.collection.database)},get deleted(){if(this.isInstanceOfRxDocument)return this._data._deleted},getLatest(){var e=this.collection._docCache.getLatestDocumentData(this.primary);return this.collection._docCache.getCachedRxDocument(e)},get $(){var e=this.primary;return this.collection.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal),(0,$t.T)(t=>t.events.find(t=>t.documentId===e)),(0,Nt.p)(e=>!!e),(0,$t.T)(e=>Tt((0,me.ZN)(e))),(0,Bt.Z)(this.collection._docCache.getLatestDocumentData(e)),(0,Mt.F)((e,t)=>e._rev===t._rev),(0,$t.T)(e=>this.collection._docCache.getCachedRxDocument(e)),(0,At.t)(me.bz))},get $$(){var e=this;return e.collection.database.getReactivityFactory().fromObservable(e.$,e.getLatest()._data,e.collection.database)},get$(e){if(x.isDevMode()){if(e.includes(".item."))throw R("DOC1",{path:e});if(e===this.primaryPath)throw R("DOC2");if(this.collection.schema.finalFields.includes(e))throw R("DOC3",{path:e});if(!B(this.collection.schema.jsonSchema,e))throw R("DOC4",{path:e})}return this.$.pipe((0,$t.T)(t=>v(t,e)),(0,Mt.F)())},get$$(e){var t=this.get$(e);return this.collection.database.getReactivityFactory().fromObservable(t,this.getLatest().get(e),this.collection.database)},populate(e){var t=B(this.collection.schema.jsonSchema,e),r=this.get(e);if(!r)return ue.$A;if(!t)throw R("DOC5",{path:e});if(!t.ref)throw R("DOC6",{path:e,schemaObj:t});var a=this.collection.database.collections[t.ref];if(!a)throw R("DOC7",{ref:t.ref,path:e,schemaObj:t});return"array"===t.type?a.findByIds(r).exec().then(e=>{var t=e.values();return Array.from(t)}):a.findOne(r).exec()},get(e){return Ut(this,e)},toJSON(e=!1){if(e)return x.deepFreezeWhenDevMode(this._data);var t=s(this._data);return delete t._rev,delete t._attachments,delete t._deleted,delete t._meta,x.deepFreezeWhenDevMode(t)},toMutableJSON(e=!1){return c(this.toJSON(e))},update(e){throw qt("update")},incrementalUpdate(e){throw qt("update")},updateCRDT(e){throw qt("crdt")},putAttachment(){throw qt("attachments")},putAttachmentBase64(){throw qt("attachments")},getAttachment(){throw qt("attachments")},allAttachments(){throw qt("attachments")},get allAttachments$(){throw qt("attachments")},async modify(e,t){var r=this._data,a=await Ht(e)(r);return this._saveData(a,r)},incrementalModify(e,t){return this.collection.incrementalWriteQueue.addWrite(this._data,Ht(e)).then(e=>this.collection._docCache.getCachedRxDocument(e))},patch(e){var t=this._data,r=c(t);return Object.entries(e).forEach(([e,t])=>{r[e]=t}),this._saveData(r,t)},incrementalPatch(e){return this.incrementalModify(t=>(Object.entries(e).forEach(([e,r])=>{t[e]=r}),t))},async _saveData(e,t){if(e=s(e),this._data._deleted)throw R("DOC11",{id:this.primary,document:this});await Zt(this.collection,e,t);var r=[{previous:t,document:e}],a=await this.collection.storageInstance.bulkWrite(r,"rx-document-save-data"),n=a.error[0];return ut(this.collection,this.primary,e,n),await this.collection._runHooks("post","save",e,this),this.collection._docCache.getCachedRxDocument(vt(this.collection.schema.primaryPath,r,a)[0])},async remove(){if(this.deleted)return Promise.reject(R("DOC13",{document:this,id:this.primary}));var e=await this.collection.bulkRemove([this]);if(e.error.length>0){var t=e.error[0];ut(this.collection,this.primary,this._data,t)}return e.success[0]},incrementalRemove(){return this.incrementalModify(async e=>(await this.collection._runHooks("pre","remove",e,this),e._deleted=!0,e)).then(async e=>(await this.collection._runHooks("post","remove",e._data,e),e))},close(){throw R("DOC14")}};function zt(e=Kt){var t=function(e,t){this.collection=e,this._data=t,this._propertyCache=new Map,this.isInstanceOfRxDocument=!0};return t.prototype=e,t}function Zt(e,t,r){return t._meta=Object.assign({},r._meta,t._meta),x.isDevMode()&&e.schema.validateChange(r,t),e._runHooks("pre","save",t,r)}function Ut(e,t){return i(e._propertyCache,t,()=>{var r=v(e._data,t);return"object"!=typeof r||null===r||Array.isArray(r)?x.deepFreezeWhenDevMode(r):new Proxy(s(r),{get(r,a){if("string"!=typeof a)return r[a];var n=a.charAt(a.length-1);if("$"===n){if(a.endsWith("$$")){var i=a.slice(0,-2);return e.get$$((0,N.L$)(t+"."+i))}var s=a.slice(0,-1);return e.get$((0,N.L$)(t+"."+s))}if("_"===n){var o=a.slice(0,-1);return e.populate((0,N.L$)(t+"."+o))}var c=r[a];return"number"==typeof c||"string"==typeof c||"boolean"==typeof c?c:Ut(e,(0,N.L$)(t+"."+a))}})})}var Vt=r(2198),Jt=r(4157),Gt=r(2442),Xt=r(6114);function Yt(e,t){return t.sort&&0!==t.sort.length?t.sort.map(e=>Object.keys(e)[0]):[e]}var er=new WeakMap;function tr(e,t){if(!e.collection.database.eventReduce)return{runFullQueryAgain:!0};for(var r=function(e){return i(er,e,()=>{var t=e.collection,r=Ze(t.storageInstance.schema,c(e.mangoQuery)),a=t.schema.primaryPath,n=Ue(t.schema.jsonSchema,r),i=Ve(t.schema.jsonSchema,r);return{primaryKey:e.collection.schema.primaryPath,skip:r.skip,limit:r.limit,sortFields:Yt(a,r),sortComparator:(t,r)=>{var a={docA:t,docB:r,rxQuery:e};return n(a.docA,a.docB)},queryMatcher:t=>i({doc:t,rxQuery:e}.doc)}})}(e),a=(0,me.ZN)(e._result).docsData.slice(0),n=(0,me.ZN)(e._result).docsDataMap,s=!1,o=[],u=0;u{var t={queryParams:r,changeEvent:e,previousResults:a,keyDocumentMap:n},i=(0,Xt.kC)(t);return"runFullQueryAgain"===i||("doNothing"!==i?(s=!0,(0,Xt.Cs)(i,r,e,a,n),!1):void 0)});return l?{runFullQueryAgain:!0}:{runFullQueryAgain:!1,changed:s,newResults:a}}var rr=function(){function e(){this._map=new Map}return e.prototype.getByQuery=function(e){var t=e.toString();return i(this._map,t,()=>e)},e}();function ar(e,t){t.uncached=!0;var r=t.toString();e._map.delete(r)}function nr(e){return e.refCount$.observers.length}var ir,sr,or=(ir=100,sr=3e4,(e,t)=>{if(!(t._map.size0||(0===i._lastEnsureEqual&&i._creationTimee._lastEnsureEqual-t._lastEnsureEqual).slice(0,s).forEach(e=>ar(t,e))}}),cr=new WeakSet;var ur=function(){function e(e,t,r){this.cacheItemByDocId=new Map,this.tasks=new Set,this.registry="function"==typeof FinalizationRegistry?new FinalizationRegistry(e=>{var t=e.docId,r=this.cacheItemByDocId.get(t);r&&(r[0].delete(e.revisionHeight+e.lwt+""),0===r[0].size&&this.cacheItemByDocId.delete(t))}):void 0,this.primaryPath=e,this.changes$=t,this.documentCreator=r,t.subscribe(e=>{this.tasks.add(()=>{for(var t=this.cacheItemByDocId,r=0;r{this.processTasks()})})}var t=e.prototype;return t.processTasks=function(){0!==this.tasks.size&&(Array.from(this.tasks).forEach(e=>e()),this.tasks.clear())},t.getLatestDocumentData=function(e){return this.processTasks(),n(this.cacheItemByDocId,e)[1]},t.getLatestDocumentDataIfExists=function(e){this.processTasks();var t=this.cacheItemByDocId.get(e);if(t)return t[1]},(0,b.A)(e,[{key:"getCachedRxDocuments",get:function(){return u(this,"getCachedRxDocuments",hr(this))}},{key:"getCachedRxDocument",get:function(){var e=hr(this);return u(this,"getCachedRxDocument",t=>e([t])[0])}}])}();function hr(e){var t=e.primaryPath,r=e.cacheItemByDocId,a=e.registry,n=x.deepFreezeWhenDevMode,i=e.documentCreator;return s=>{for(var o=new Array(s.length),c=[],u=0;u0&&a&&(e.tasks.add(()=>{for(var e=0;e{e.processTasks()})),o}}function lr(e,t){return(0,e.getCachedRxDocuments)(t)}var dr="function"==typeof WeakRef?function(e){return new WeakRef(e)}:function(e){return{deref:()=>e}};var mr=function(){function e(e,t,r){this.time=ie(),this.query=e,this.count=r,this.documents=lr(this.query.collection._docCache,t)}return e.prototype.getValue=function(e){var t=this.query.op;if("count"===t)return this.count;if("findOne"===t){var r=0===this.documents.length?null:this.documents[0];if(!r&&e)throw R("QU10",{collection:this.query.collection.name,query:this.query.mangoQuery,op:t});return r}return"findByIds"===t?this.docsMap:this.documents.slice(0)},(0,b.A)(e,[{key:"docsData",get:function(){return u(this,"docsData",this.documents.map(e=>e._data))}},{key:"docsDataMap",get:function(){var e=new Map;return this.documents.forEach(t=>{e.set(t.primary,t._data)}),u(this,"docsDataMap",e)}},{key:"docsMap",get:function(){for(var e=new Map,t=this.documents,r=0;r"string"!=typeof e))return r.$eq}return!1}(this.collection.schema.primaryPath,t)}var t=e.prototype;return t._setResultData=function(e){if(void 0===e)throw R("QU18",{database:this.collection.database.name,collection:this.collection.name});if("number"!=typeof e){e instanceof Map&&(e=Array.from(e.values()));var t=new mr(this,e,e.length);this._result=t}else this._result=new mr(this,[],e)},t._execOverDatabase=async function(){if(this._execOverDatabaseCount=this._execOverDatabaseCount+1,"count"===this.op){var e=this.getPreparedQuery(),t=await this.collection.storageInstance.count(e);if("slow"!==t.mode||this.collection.database.allowSlowCount)return{result:t.count,counter:this.collection._changeEventBuffer.getCounter()};throw R("QU14",{collection:this.collection,queryObj:this.mangoQuery})}if("findByIds"===this.op){var r=(0,me.ZN)(this.mangoQuery.selector)[this.collection.schema.primaryPath].$in,a=new Map,n=[];if(r.forEach(e=>{var t=this.collection._docCache.getLatestDocumentDataIfExists(e);if(t){if(!t._deleted){var r=this.collection._docCache.getCachedRxDocument(t);a.set(e,r)}}else n.push(e)}),n.length>0)(await this.collection.storageInstance.findDocumentsById(n,!1)).forEach(e=>{var t=this.collection._docCache.getCachedRxDocument(e);a.set(t.primary,t)});return{result:a,counter:this.collection._changeEventBuffer.getCounter()}}var i=await gr(this);return{result:i.docs,counter:i.counter}},t.exec=async function(e){if(e&&"findOne"!==this.op)throw R("QU9",{collection:this.collection.name,query:this.mangoQuery,op:this.op});return await yr(this),(0,me.ZN)(this._result).getValue(e)},t.toString=function(){var e=o({op:this.op,query:Ze(this.collection.schema.jsonSchema,this.mangoQuery),other:this.other},!0),t=JSON.stringify(e);return this.toString=()=>t,t},t.getPreparedQuery=function(){var e={rxQuery:this,mangoQuery:Ze(this.collection.schema.jsonSchema,this.mangoQuery)};e.mangoQuery.selector._deleted={$eq:!1},e.mangoQuery.index&&e.mangoQuery.index.unshift("_deleted"),it("prePrepareQuery",e);var t=Ge(this.collection.schema.jsonSchema,e.mangoQuery);return this.getPreparedQuery=()=>t,t},t.doesDocumentDataMatch=function(e){return!e._deleted&&this.queryMatcher(e)},t.remove=async function(){var e=await this.exec();if(Array.isArray(e)){var t=await this.collection.bulkRemove(e);if(t.error.length>0)throw P(t.error[0]);return t.success}return e.remove()},t.incrementalRemove=function(){return Je(this.asRxQuery,e=>e.incrementalRemove())},t.update=function(e){throw qt("update")},t.patch=function(e){return Je(this.asRxQuery,t=>t.patch(e))},t.incrementalPatch=function(e){return Je(this.asRxQuery,t=>t.incrementalPatch(e))},t.modify=function(e){return Je(this.asRxQuery,t=>t.modify(e))},t.incrementalModify=function(e){return Je(this.asRxQuery,t=>t.incrementalModify(e))},t.where=function(e){throw qt("query-builder")},t.sort=function(e){throw qt("query-builder")},t.skip=function(e){throw qt("query-builder")},t.limit=function(e){throw qt("query-builder")},(0,b.A)(e,[{key:"$",get:function(){if(!this._$){var e=this.collection.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal),(0,Bt.Z)(null),(0,Gt.Z)(()=>yr(this)),(0,$t.T)(()=>this._result),(0,At.t)(me.bz),(0,Mt.F)((e,t)=>!(!e||e.time!==(0,me.ZN)(t).time)),(0,Nt.p)(e=>!!e),(0,$t.T)(e=>(0,me.ZN)(e).getValue()));this._$=(0,Jt.h)(e,this.refCount$.pipe((0,Nt.p)(()=>!1)))}return this._$}},{key:"$$",get:function(){return this.collection.database.getReactivityFactory().fromObservable(this.$,void 0,this.collection.database)}},{key:"queryMatcher",get:function(){this.collection.schema.jsonSchema;return u(this,"queryMatcher",Ve(0,Ze(this.collection.schema.jsonSchema,this.mangoQuery)))}},{key:"asRxQuery",get:function(){return this}}])}();function vr(e,t,r,a){it("preCreateRxQuery",{op:e,queryObj:t,collection:r,other:a});var n,i,s=new pr(e,t,r,a);return s=(n=s).collection._queryCache.getByQuery(n),i=r,cr.has(i)||(cr.add(i),(0,ue.dY)().then(()=>(0,ue.Ve)(200)).then(()=>{i.closed||i.cacheReplacementPolicy(i,i._queryCache),cr.delete(i)})),s}async function yr(e){return e.collection.awaitBeforeReads.size>0&&await Promise.all(Array.from(e.collection.awaitBeforeReads).map(e=>e())),e._ensureEqualQueue=e._ensureEqualQueue.then(()=>function(e){if(e._lastEnsureEqual=ie(),e.collection.database.closed||function(e){var t=e.asRxQuery.collection._changeEventBuffer.getCounter();return e._latestChangeEvent>=t}(e))return ue.Dr;var t=!1,r=!1;-1===e._latestChangeEvent&&(r=!0);if(!r){var a=e.asRxQuery.collection._changeEventBuffer.getFrom(e._latestChangeEvent+1);if(null===a)r=!0;else{e._latestChangeEvent=e.asRxQuery.collection._changeEventBuffer.getCounter();var n=e.asRxQuery.collection._changeEventBuffer.reduceByLastOfDoc(a);if("count"===e.op){var i=(0,me.ZN)(e._result).count,s=i;n.forEach(t=>{var r=t.previousDocumentData&&e.doesDocumentDataMatch(t.previousDocumentData),a=e.doesDocumentDataMatch(t.documentData);!r&&a&&s++,r&&!a&&s--}),s!==i&&(t=!0,e._setResultData(s))}else{var o=tr(e,n);o.runFullQueryAgain?r=!0:o.changed&&(t=!0,e._setResultData(o.newResults))}}}if(r)return e._execOverDatabase().then(r=>{var a=r.result;return e._latestChangeEvent=r.counter,"number"==typeof a?(e._result&&a===e._result.count||(t=!0,e._setResultData(a)),t):(e._result&&function(e,t,r){if(t.length!==r.length)return!1;for(var a=0,n=t.length;a{a++});if(e.isFindOneByIdQuery)if(Array.isArray(e.isFindOneByIdQuery)){var i=e.isFindOneByIdQuery;if(i=i.filter(r=>{var a=e.collection._docCache.getLatestDocumentDataIfExists(r);return!a||(a._deleted||t.push(a),!1)}),i.length>0){var s=await r.storageInstance.findDocumentsById(i,!1);t=t.concat(s)}}else{var o=e.isFindOneByIdQuery,c=e.collection._docCache.getLatestDocumentDataIfExists(o);if(!c){var u=await r.storageInstance.findDocumentsById([o],!1);u[0]&&(c=u[0])}c&&!c._deleted&&t.push(c)}else{var h=e.getPreparedQuery(),l=await r.storageInstance.query(h);t=l.documents}return n.unsubscribe(),a>0?(await(0,ue.ND)(0),gr(e)):{docs:t,counter:r._changeEventBuffer.getCounter()}}var br="collection",wr="storage-token",Dr=q({version:0,title:"RxInternalDocument",primaryKey:{key:"id",fields:["context","key"],separator:"|"},type:"object",properties:{id:{type:"string",maxLength:200},key:{type:"string"},context:{type:"string",enum:[br,wr,"rx-migration-status","rx-pipeline-checkpoint","OTHER"]},data:{type:"object",additionalProperties:!0}},indexes:[],required:["key","context","data"],additionalProperties:!1,sharding:{shards:1,mode:"collection"}});function xr(e,t){return A(Dr,{key:e,context:t})}async function _r(e){var t=Ge(e.schema,{selector:{context:br,_deleted:{$eq:!1}},sort:[{id:"asc"}],skip:0});return(await e.query(t)).documents}var kr="storageToken",Ir=xr(kr,wr);function Er(e,t){return e+"-"+t.version}function Cr(e,t){return t=function(e,t){for(var r=Object.keys(e.defaultValues),a=0;ae.data.name===n),u=[];c.forEach(e=>{u.push({collectionName:e.data.name,schema:e.data.schema,isCollection:!0}),e.data.connectedStorages.forEach(e=>u.push({collectionName:e.collectionName,isCollection:!1,schema:e.schema}))});var h=new Set;if(u=u.filter(e=>{var t=e.collectionName+"||"+e.schema.version;return!h.has(t)&&(h.add(t),!0)}),await Promise.all(u.map(async t=>{var o=await e.createStorageInstance({collectionName:t.collectionName,databaseInstanceToken:r,databaseName:a,multiInstance:i,options:{},schema:t.schema,password:s,devMode:x.isDevMode()});await o.remove(),t.isCollection&&await st("postRemoveRxCollection",{storage:e,databaseName:a,collectionName:n})})),o){var l=c.map(e=>{var t=dt(e);return t._deleted=!0,t._meta.lwt=ie(),t._rev=at(r,e),{previous:e,document:t}});await t.bulkWrite(l,"rx-database-remove-collection-all")}}function Or(e){if(e.closed)throw R("COL21",{collection:e.name,version:e.schema.version})}var Sr=function(){function e(e){this.subs=[],this.counter=0,this.eventCounterMap=new WeakMap,this.buffer=[],this.limit=100,this.tasks=new Set,this.collection=e,this.subs.push(this.collection.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal)).subscribe(e=>{this.tasks.add(()=>this._handleChangeEvents(e.events)),this.tasks.size<=1&&(0,ue.vN)().then(()=>{this.processTasks()})}))}var t=e.prototype;return t.processTasks=function(){0!==this.tasks.size&&(Array.from(this.tasks).forEach(e=>e()),this.tasks.clear())},t._handleChangeEvents=function(e){var t=this.counter;this.counter=this.counter+e.length,e.length>this.limit?this.buffer=e.slice(-1*e.length):(this.buffer=this.buffer.concat(e),this.buffer=this.buffer.slice(-1*this.limit));for(var r=t+1,a=this.eventCounterMap,n=0;nt(e))},t.reduceByLastOfDoc=function(e){return this.processTasks(),e.slice(0)},t.close=function(){this.tasks.clear(),this.subs.forEach(e=>e.unsubscribe())},e}();var jr=new WeakMap;function Pr(e){var t=e.schema.getDocumentPrototype(),r=function(e){var t={};return Object.entries(e.methods).forEach(([e,r])=>{t[e]=r}),t}(e),a={};return[t,r,Kt].forEach(e=>{Object.getOwnPropertyNames(e).forEach(t=>{var r=Object.getOwnPropertyDescriptor(e,t),n=!0;(t.startsWith("_")||t.endsWith("_")||t.startsWith("$")||t.endsWith("$"))&&(n=!1),"function"==typeof r.value?Object.defineProperty(a,t,{get(){return r.value.bind(this)},enumerable:n,configurable:!1}):(r.enumerable=n,r.configurable=!1,r.writable&&(r.writable=!1),Object.defineProperty(a,t,r))})}),a}function $r(e,t,r){var a=function(e,t,r){var a=new e(t,r);return it("createRxDocument",a),a}(t,e,x.deepFreezeWhenDevMode(r));return e._runHooksSync("post","create",r,a),it("postCreateRxDocument",a),a}var Nr={isEqual:(e,t,r)=>(e=Br(e),t=Br(t),(0,St.b)(lt(e),lt(t))),resolve:e=>e.realMasterState};function Br(e){return e._attachments||((e=s(e))._attachments={}),e}var Mr=["pre","post"],Ar=["insert","save","remove","create"],qr=!1,Tr=new Set,Lr=function(){function e(e,t,r,a,n={},i={},s={},o={},c={},u=or,h={},l=Nr){this.storageInstance={},this.timeouts=new Set,this.incrementalWriteQueue={},this.awaitBeforeReads=new Set,this._incrementalUpsertQueues=new Map,this.synced=!1,this.hooks={},this._subs=[],this._docCache={},this._queryCache=new rr,this.$={},this.checkpoint$={},this._changeEventBuffer={},this.eventBulks$={},this.onClose=[],this.closed=!1,this.onRemove=[],this.database=e,this.name=t,this.schema=r,this.internalStorageInstance=a,this.instanceCreationOptions=n,this.migrationStrategies=i,this.methods=s,this.attachments=o,this.options=c,this.cacheReplacementPolicy=u,this.statics=h,this.conflictHandler=l,function(e){if(qr)return;qr=!0;var t=Object.getPrototypeOf(e);Ar.forEach(e=>{Mr.map(r=>{var a=r+(0,N.Z2)(e);t[a]=function(t,a){return this.addHook(r,e,t,a)}})})}(this.asRxCollection),e&&(this.eventBulks$=e.eventBulks$.pipe((0,Nt.p)(e=>e.collectionName===this.name))),this.database&&Tr.add(this)}var t=e.prototype;return t.prepare=async function(){if(!await de()){for(var e=0;e<10&&Tr.size>16;)e++,await this.promiseWait(30);if(Tr.size>16)throw R("COL23",{database:this.database.name,collection:this.name,args:{existing:Array.from(Tr.values()).map(e=>({db:e.database?e.database.name:"",c:e.name}))}})}var t,r;this.storageInstance=mt(this.database,this.internalStorageInstance,this.schema.jsonSchema),this.incrementalWriteQueue=new Ft(this.storageInstance,this.schema.primaryPath,(e,t)=>Zt(this,e,t),e=>this._runHooks("post","save",e)),this.$=this.eventBulks$.pipe((0,Gt.Z)(e=>Wt(e))),this.checkpoint$=this.eventBulks$.pipe((0,$t.T)(e=>e.checkpoint)),this._changeEventBuffer=(t=this.asRxCollection,new Sr(t)),this._docCache=new ur(this.schema.primaryPath,this.eventBulks$.pipe((0,Nt.p)(e=>!e.isLocal),(0,$t.T)(e=>e.events)),e=>{var t;return r||(t=this.asRxCollection,r=i(jr,t,()=>zt(Pr(t)))),$r(this.asRxCollection,r,e)});var a=this.database.internalStore.changeStream().pipe((0,Nt.p)(e=>{var t=this.name+"-"+this.schema.version;return!!e.events.find(e=>"collection"===e.documentData.context&&e.documentData.key===t&&"DELETE"===e.operation)})).subscribe(async()=>{await this.close(),await Promise.all(this.onRemove.map(e=>e()))});this._subs.push(a);var n=await this.database.storageToken,s=this.storageInstance.changeStream().subscribe(e=>{var t={id:e.id,isLocal:!1,internal:!1,collectionName:this.name,storageToken:n,events:e.events,databaseToken:this.database.token,checkpoint:e.checkpoint,context:e.context};this.database.$emit(t)});return this._subs.push(s),ue.em},t.cleanup=function(e){throw Or(this),qt("cleanup")},t.migrationNeeded=function(){throw qt("migration-schema")},t.getMigrationState=function(){throw qt("migration-schema")},t.startMigration=function(e=10){return Or(this),this.getMigrationState().startMigration(e)},t.migratePromise=function(e=10){return this.getMigrationState().migratePromise(e)},t.insert=async function(e){Or(this);var t=await this.bulkInsert([e]),r=t.error[0];return ut(this,e[this.schema.primaryPath],e,r),(0,me.ZN)(t.success[0])},t.insertIfNotExists=async function(e){var t=await this.bulkInsert([e]);if(t.error.length>0){var r=t.error[0];if(409===r.status){var a=r.documentInDb;return lr(this._docCache,[a])[0]}throw r}return t.success[0]},t.bulkInsert=async function(e){if(Or(this),0===e.length)return{success:[],error:[]};var t,r=this.schema.primaryPath,a=new Set;if(this.hasHooks("pre","insert"))t=await Promise.all(e.map(e=>{var t=Cr(this.schema,e);return this._runHooks("pre","insert",t).then(()=>(a.add(t[r]),{document:t}))}));else{t=new Array(e.length);for(var n=this.schema,i=0;i{var t=e.document;l.set(t[r],t)}),await Promise.all(h.success.map(e=>this._runHooks("post","insert",l.get(e.primary),e)))}return h},t.bulkRemove=async function(e){Or(this);var t,r=this.schema.primaryPath;if(0===e.length)return{success:[],error:[]};"string"==typeof e[0]?t=await this.findByIds(e).exec():(t=new Map,e.forEach(e=>t.set(e.primary,e)));var a=[],n=new Map;Array.from(t.values()).forEach(e=>{var t=e.toMutableJSON(!0);a.push(t),n.set(e.primary,t)}),await Promise.all(a.map(e=>{var r=e[this.schema.primaryPath];return this._runHooks("pre","remove",e,t.get(r))}));var i=a.map(e=>{var t=s(e);return t._deleted=!0,{previous:e,document:t}}),o=await this.storageInstance.bulkWrite(i,"rx-collection-bulk-remove"),c=vt(this.schema.primaryPath,i,o),u=[],h=c.map(e=>{var t=e[r],a=this._docCache.getCachedRxDocument(e);return u.push(a),t});return await Promise.all(h.map(e=>this._runHooks("post","remove",n.get(e),t.get(e)))),{success:u,error:o.error}},t.bulkUpsert=async function(e){Or(this);var t=[],r=new Map;e.forEach(e=>{var a=Cr(this.schema,e),n=a[this.schema.primaryPath];if(!n)throw R("COL3",{primaryPath:this.schema.primaryPath,data:a,schema:this.schema.jsonSchema});r.set(n,a),t.push(a)});var a=await this.bulkInsert(t),i=a.success.slice(0),s=[];return await Promise.all(a.error.map(async e=>{if(409!==e.status)s.push(e);else{var t=e.documentId,a=n(r,t),o=(0,me.ZN)(e.documentInDb),c=this._docCache.getCachedRxDocuments([o])[0],u=await c.incrementalModify(()=>a);i.push(u)}})),{error:s,success:i}},t.upsert=async function(e){Or(this);var t=await this.bulkUpsert([e]);return ut(this.asRxCollection,e[this.schema.primaryPath],e,t.error[0]),t.success[0]},t.incrementalUpsert=function(e){Or(this);var t=Cr(this.schema,e),r=t[this.schema.primaryPath];if(!r)throw R("COL4",{data:e});var a=this._incrementalUpsertQueues.get(r);return a||(a=ue.em),a=a.then(()=>function(e,t,r){var a=e._docCache.getLatestDocumentDataIfExists(t);if(a)return Promise.resolve({doc:e._docCache.getCachedRxDocuments([a])[0],inserted:!1});return e.findOne(t).exec().then(t=>t?{doc:t,inserted:!1}:e.insert(r).then(e=>({doc:e,inserted:!0})))}(this,r,t)).then(e=>e.inserted?e.doc:function(e,t){return e.incrementalModify(e=>t)}(e.doc,t)),this._incrementalUpsertQueues.set(r,a),a},t.find=function(e){return Or(this),it("prePrepareRxQuery",{op:"find",queryObj:e,collection:this}),e||(e={selector:{}}),vr("find",e,this)},t.findOne=function(e){var t;if(Or(this),it("prePrepareRxQuery",{op:"findOne",queryObj:e,collection:this}),"string"==typeof e)t=vr("findOne",{selector:{[this.schema.primaryPath]:e},limit:1},this);else{if(e||(e={selector:{}}),e.limit)throw R("QU6");(e=s(e)).limit=1,t=vr("findOne",e,this)}return t},t.count=function(e){return Or(this),e||(e={selector:{}}),vr("count",e,this)},t.findByIds=function(e){return Or(this),vr("findByIds",{selector:{[this.schema.primaryPath]:{$in:e.slice(0)}}},this)},t.exportJSON=function(){throw qt("json-dump")},t.importJSON=function(e){throw qt("json-dump")},t.insertCRDT=function(e){throw qt("crdt")},t.addPipeline=function(e){throw qt("pipeline")},t.addHook=function(e,t,r,a=!1){if("function"!=typeof r)throw O("COL7",{key:t,when:e});if(!Mr.includes(e))throw O("COL8",{key:t,when:e});if(!Ar.includes(t))throw R("COL9",{key:t});if("post"===e&&"create"===t&&!0===a)throw R("COL10",{when:e,key:t,parallel:a});var n=r.bind(this),i=a?"parallel":"series";this.hooks[t]=this.hooks[t]||{},this.hooks[t][e]=this.hooks[t][e]||{series:[],parallel:[]},this.hooks[t][e][i].push(n)},t.getHooks=function(e,t){return this.hooks[t]&&this.hooks[t][e]?this.hooks[t][e]:{series:[],parallel:[]}},t.hasHooks=function(e,t){if(!this.hooks[t]||!this.hooks[t][e])return!1;var r=this.getHooks(e,t);return!!r&&(r.series.length>0||r.parallel.length>0)},t._runHooks=function(e,t,r,a){var n=this.getHooks(e,t);if(!n)return ue.em;var i=n.series.map(e=>()=>e(r,a));return(0,ue.h$)(i).then(()=>Promise.all(n.parallel.map(e=>e(r,a))))},t._runHooksSync=function(e,t,r,a){if(this.hasHooks(e,t)){var n=this.getHooks(e,t);n&&n.series.forEach(e=>e(r,a))}},t.promiseWait=function(e){return new Promise(t=>{var r=setTimeout(()=>{this.timeouts.delete(r),t()},e);this.timeouts.add(r)})},t.close=async function(){return this.closed?ue.Dr:(Tr.delete(this),await Promise.all(this.onClose.map(e=>e())),this.closed=!0,Array.from(this.timeouts).forEach(e=>clearTimeout(e)),this._changeEventBuffer&&this._changeEventBuffer.close(),this.database.requestIdlePromise().then(()=>this.storageInstance.close()).then(()=>(this._subs.forEach(e=>e.unsubscribe()),delete this.database.collections[this.name],this.database.collectionsSubject$.next({collection:this.asRxCollection,type:"CLOSED"}),st("postCloseRxCollection",this).then(()=>!0))))},t.remove=async function(){await this.close(),await Promise.all(this.onRemove.map(e=>e())),await Rr(this.database.storage,this.database.internalStore,this.database.token,this.database.name,this.name,this.database.multiInstance,this.database.password,this.database.hashFunction)},(0,b.A)(e,[{key:"insert$",get:function(){return this.$.pipe((0,Nt.p)(e=>"INSERT"===e.operation))}},{key:"update$",get:function(){return this.$.pipe((0,Nt.p)(e=>"UPDATE"===e.operation))}},{key:"remove$",get:function(){return this.$.pipe((0,Nt.p)(e=>"DELETE"===e.operation))}},{key:"asRxCollection",get:function(){return this}}])}();var Qr=r(7635),Wr=r(5525),Fr=new Set,Hr=new Map,Kr=function(){function e(e,t,r,a,n,i,s=!1,o={},c,u,h,l,d,m){this.idleQueue=new Qr.G,this.rxdbVersion=Ct,this.storageInstances=new Set,this._subs=[],this.startupErrors=[],this.onClose=[],this.closed=!1,this.collections={},this.states={},this.eventBulks$=new ae.B,this.closePromise=null,this.collectionsSubject$=new ae.B,this.observable$=this.eventBulks$.pipe((0,Gt.Z)(e=>Wt(e))),this.storageToken=ue.Dr,this.storageTokenDocument=ue.Dr,this.emittedEventBulkIds=new Wr.p4(6e4),this.name=e,this.token=t,this.storage=r,this.instanceCreationOptions=a,this.password=n,this.multiInstance=i,this.eventReduce=s,this.options=o,this.internalStore=c,this.hashFunction=u,this.cleanupPolicy=h,this.allowSlowCount=l,this.reactivity=d,this.onClosed=m,"pseudoInstance"!==this.name&&(this.internalStore=mt(this.asRxDatabase,c,Dr),this.storageTokenDocument=async function(e){var t=(0,N.zs)(10),r=e.password?await e.hashFunction(JSON.stringify(e.password)):void 0,a=[{document:{id:Ir,context:wr,key:kr,data:{rxdbVersion:e.rxdbVersion,token:t,instanceToken:e.token,passwordHash:r},_deleted:!1,_meta:{lwt:1},_rev:"",_attachments:{}}}],n=await e.internalStore.bulkWrite(a,"internal-add-storage-token");if(!n.error[0])return vt("id",a,n)[0];var i=(0,me.ZN)(n.error[0]);if(i.isError&&S(i)){var s=i;if(!function(e,t){if(!e)return!1;var r=e.split(".")[0],a=t.split(".")[0];return"16"===r&&"17"===a||r===a}(s.documentInDb.data.rxdbVersion,e.rxdbVersion))throw R("DM5",{args:{database:e.name,databaseStateVersion:s.documentInDb.data.rxdbVersion,codeVersion:e.rxdbVersion}});if(r&&r!==s.documentInDb.data.passwordHash)throw R("DB1",{passwordHash:r,existingPasswordHash:s.documentInDb.data.passwordHash});var o=s.documentInDb;return(0,me.ZN)(o)}throw i}(this.asRxDatabase).catch(e=>this.startupErrors.push(e)),this.storageToken=this.storageTokenDocument.then(e=>e.data.token).catch(e=>this.startupErrors.push(e)))}var t=e.prototype;return t.getReactivityFactory=function(){if(!this.reactivity)throw R("DB14",{database:this.name});return this.reactivity},t[Symbol.asyncDispose]=async function(){await this.close()},t.$emit=function(e){this.emittedEventBulkIds.has(e.id)||(this.emittedEventBulkIds.add(e.id),this.eventBulks$.next(e))},t.removeCollectionDoc=async function(e,t){var r=await ot(this.internalStore,xr(Er(e,t),br));if(!r)throw R("SNH",{name:e,schema:t});var a=dt(r);a._deleted=!0,await this.internalStore.bulkWrite([{document:a,previous:r}],"rx-database-remove-collection")},t.addCollections=async function(e){var t={},r={},a=[],n={};await Promise.all(Object.entries(e).map(async([e,i])=>{var o=e,c=i.schema;t[o]=c;var u=Pt(c,this.hashFunction);if(r[o]=u,this.collections[e])throw R("DB3",{name:e});var h=Er(e,c),l={id:xr(h,br),key:h,context:br,data:{name:o,schemaHash:await u.hash,schema:u.jsonSchema,version:u.version,connectedStorages:[]},_deleted:!1,_meta:{lwt:1},_rev:"",_attachments:{}};a.push({document:l});var d=Object.assign({},i,{name:o,schema:u,database:this}),m=s(i);m.database=this,m.name=e,it("preCreateRxCollection",m),d.conflictHandler=m.conflictHandler,n[o]=d}));var i=await this.internalStore.bulkWrite(a,"rx-database-add-collection");await async function(e){if(await e.storageToken,e.startupErrors[0])throw e.startupErrors[0]}(this),await Promise.all(i.error.map(async e=>{if(409!==e.status)throw R("DB12",{database:this.name,writeError:e});var a=(0,me.ZN)(e.documentInDb),n=a.data.name,i=r[n];if(a.data.schemaHash!==await i.hash)throw R("DB6",{database:this.name,collection:n,previousSchemaHash:a.data.schemaHash,schemaHash:await i.hash,previousSchema:a.data.schema,schema:(0,me.ZN)(t[n])})}));var o={};return await Promise.all(Object.keys(e).map(async e=>{var t=n[e],r=await async function({database:e,name:t,schema:r,instanceCreationOptions:a={},migrationStrategies:n={},autoMigrate:i=!0,statics:s={},methods:o={},attachments:c={},options:u={},localDocuments:h=!1,cacheReplacementPolicy:l=or,conflictHandler:d=Nr}){var m={databaseInstanceToken:e.token,databaseName:e.name,collectionName:t,schema:r.jsonSchema,options:a,multiInstance:e.multiInstance,password:e.password,devMode:x.isDevMode()};it("preCreateRxStorageInstance",m);var f=await async function(e,t){return t.multiInstance=e.multiInstance,await e.storage.createStorageInstance(t)}(e,m),p=new Lr(e,t,r,f,a,n,o,c,u,l,s,d);try{await p.prepare(),Object.entries(s).forEach(([e,t])=>{Object.defineProperty(p,e,{get:()=>t.bind(p)})}),it("createRxCollection",{collection:p,creator:{name:t,schema:r,storageInstance:f,instanceCreationOptions:a,migrationStrategies:n,methods:o,attachments:c,options:u,cacheReplacementPolicy:l,localDocuments:h,statics:s}}),i&&0!==p.schema.version&&await p.migratePromise()}catch(v){throw Tr.delete(p),await f.close(),v}return p}(t);o[e]=r,this.collections[e]=r,this.collectionsSubject$.next({collection:r,type:"ADDED"}),this[e]||Object.defineProperty(this,e,{get:()=>this.collections[e]})})),o},t.lockedRun=function(e){return this.idleQueue.wrapCall(e)},t.requestIdlePromise=function(){return this.idleQueue.requestIdlePromise()},t.exportJSON=function(e){throw qt("json-dump")},t.addState=function(e){throw qt("state")},t.importJSON=function(e){throw qt("json-dump")},t.backup=function(e){throw qt("backup")},t.leaderElector=function(){throw qt("leader-election")},t.isLeader=function(){throw qt("leader-election")},t.waitForLeadership=function(){throw qt("leader-election")},t.migrationStates=function(){throw qt("migration-schema")},t.close=function(){if(this.closePromise)return this.closePromise;var{promise:e,resolve:t}=zr(),r=e=>{this.onClosed&&this.onClosed(),this.closed=!0,t(e)};return this.closePromise=e,(async()=>{if(await st("preCloseRxDatabase",this),this.eventBulks$.complete(),this.collectionsSubject$.complete(),this._subs.map(e=>e.unsubscribe()),"pseudoInstance"!==this.name)return this.requestIdlePromise().then(()=>Promise.all(this.onClose.map(e=>e()))).then(()=>Promise.all(Object.keys(this.collections).map(e=>this.collections[e]).map(e=>e.close()))).then(()=>this.internalStore.close()).then(()=>r(!0));r(!1)})(),e},t.remove=function(){return this.close().then(()=>async function(e,t,r=!0,a){var n=(0,N.zs)(10),i=await Ur(n,t,e,{},r,a),s=await _r(i),o=new Set;s.forEach(e=>o.add(e.data.name));var c=Array.from(o);return await Promise.all(c.map(s=>Rr(t,i,n,e,s,r,a))),await st("postRemoveRxDatabase",{databaseName:e,storage:t}),await i.remove(),c}(this.name,this.storage,this.multiInstance,this.password))},(0,b.A)(e,[{key:"$",get:function(){return this.observable$}},{key:"collections$",get:function(){return this.collectionsSubject$.asObservable()}},{key:"asRxDatabase",get:function(){return this}}])}();function zr(){var e,t;return{promise:new Promise((r,a)=>{e=r,t=a}),resolve:e,reject:t}}function Zr(e,t){return t.name+"|"+e}async function Ur(e,t,r,a,n,i){return await t.createStorageInstance({databaseInstanceToken:e,databaseName:r,collectionName:"_rxdb_internal",schema:Dr,options:a,multiInstance:n,password:i,devMode:x.isDevMode()})}function Vr({storage:e,instanceCreationOptions:t,name:r,password:a,multiInstance:n=!0,eventReduce:i=!0,ignoreDuplicate:s=!1,options:o={},cleanupPolicy:c,closeDuplicates:u=!1,allowSlowCount:h=!1,localDocuments:l=!1,hashFunction:d=ce,reactivity:m}){it("preCreateRxDatabase",{storage:e,instanceCreationOptions:t,name:r,password:a,multiInstance:n,eventReduce:i,ignoreDuplicate:s,options:o,localDocuments:l});var f=Zr(r,e),p=Hr.get(f)||new Set,v=zr(),y=Array.from(p),g=()=>{p.delete(v.promise),Fr.delete(f)};return p.add(v.promise),Hr.set(f,p),(async()=>{if(u&&await Promise.all(y.map(e=>e.catch(()=>null).then(e=>e&&e.close()))),s){if(!x.isDevMode())throw R("DB9",{database:r})}else!function(e,t){if(Fr.has(Zr(e,t)))throw R("DB8",{name:e,storage:t.name,link:"https://rxdb.info/rx-database.html#ignoreduplicate"})}(r,e);Fr.add(f);var p=(0,N.zs)(10),v=await Ur(p,e,r,t,n,a),b=new Kr(r,p,e,t,a,n,i,o,v,d,c,h,m,g);return await st("createRxDatabase",{database:b,creator:{storage:e,instanceCreationOptions:t,name:r,password:a,multiInstance:n,eventReduce:i,ignoreDuplicate:s,options:o,localDocuments:l}}),b})().then(e=>{v.resolve(e)}).catch(e=>{v.reject(e),g()}),v.promise}var Jr={RxSchema:jt.prototype,RxDocument:Kt,RxQuery:pr.prototype,RxCollection:Lr.prototype,RxDatabase:Kr.prototype},Gr=new Set,Xr=new Set;var Yr=new WeakMap,ea=new WeakMap;function ta(e){var t=Yr.get(e);if(!t){var r=e.database?e.database:e,a=e.database?e.name:"";throw R("LD8",{database:r.name,collection:a})}return t}function ra(e,t,r,a,n,i){return t.createStorageInstance({databaseInstanceToken:e,databaseName:r,collectionName:ia(a),schema:sa,options:n,multiInstance:i,devMode:x.isDevMode()})}function aa(e){var t=Yr.get(e);if(t)return Yr.delete(e),t.then(e=>e.storageInstance.close())}async function na(e,t,r){var a=(0,N.zs)(10),n=await ra(a,e,t,r,{},!1);await n.remove()}function ia(e){return"plugin-local-documents-"+e}var sa=q({title:"RxLocalDocument",version:0,primaryKey:"id",type:"object",properties:{id:{type:"string",maxLength:128},data:{type:"object",additionalProperties:!0}},required:["id","data"]});async function oa(e,t){var r=await ta(this),a={id:e,data:t,_deleted:!1,_meta:{lwt:1},_rev:"",_attachments:{}};return ct(r.storageInstance,{document:a},"local-document-insert").then(e=>r.docCache.getCachedRxDocument(e))}function ca(e,t){return this.getLocal(e).then(r=>r?r.incrementalModify(()=>t):this.insertLocal(e,t))}async function ua(e){var t=await ta(this),r=t.docCache,a=r.getLatestDocumentDataIfExists(e);return a?Promise.resolve(r.getCachedRxDocument(a)):ot(t.storageInstance,e).then(e=>e?t.docCache.getCachedRxDocument(e):null)}function ha(e){return this.$.pipe((0,Bt.Z)(null),(0,Gt.Z)(async t=>t?{changeEvent:t}:{doc:await this.getLocal(e)}),(0,Gt.Z)(async t=>{if(t.changeEvent){var r=t.changeEvent;return r.isLocal&&r.documentId===e?{use:!0,doc:await this.getLocal(e)}:{use:!1}}return{use:!0,doc:t.doc}}),(0,Nt.p)(e=>e.use),(0,$t.T)(e=>e.doc))}var la=function(e){function t(t,r,a){var n;return(n=e.call(this,null,r)||this).id=t,n.parent=a,n}return(0,w.A)(t,e),t}(zt()),da={get isLocal(){return!0},get allAttachments$(){throw R("LD1",{document:this})},get primaryPath(){return"id"},get primary(){return this.id},get $(){var e=n(ea,this.parent),t=this.primary;return this.parent.eventBulks$.pipe((0,Nt.p)(e=>!!e.isLocal),(0,$t.T)(e=>e.events.find(e=>e.documentId===t)),(0,Nt.p)(e=>!!e),(0,$t.T)(e=>Tt((0,me.ZN)(e))),(0,Bt.Z)(e.docCache.getLatestDocumentData(this.primary)),(0,Mt.F)((e,t)=>e._rev===t._rev),(0,$t.T)(t=>e.docCache.getCachedRxDocument(t)),(0,At.t)(me.bz))},get $$(){var e=this,t=pa(e);return t.getReactivityFactory().fromObservable(e.$,e.getLatest()._data,t)},get deleted$$(){var e=this,t=pa(e);return t.getReactivityFactory().fromObservable(e.deleted$,e.getLatest().deleted,t)},getLatest(){var e=n(ea,this.parent),t=e.docCache.getLatestDocumentData(this.primary);return e.docCache.getCachedRxDocument(t)},get(e){if(e="data."+e,this._data){if("string"!=typeof e)throw O("LD2",{objPath:e});var t=v(this._data,e);return t=x.deepFreezeWhenDevMode(t)}},get$(e){if(e="data."+e,x.isDevMode()){if(e.includes(".item."))throw R("LD3",{objPath:e});if(e===this.primaryPath)throw R("LD4")}return this.$.pipe((0,$t.T)(e=>e._data),(0,$t.T)(t=>v(t,e)),(0,Mt.F)())},get$$(e){var t=pa(this);return t.getReactivityFactory().fromObservable(this.get$(e),this.getLatest().get(e),t)},async incrementalModify(e){var t=await ta(this.parent);return t.incrementalWriteQueue.addWrite(this._data,async t=>(t.data=await e(t.data,this),t)).then(e=>t.docCache.getCachedRxDocument(e))},incrementalPatch(e){return this.incrementalModify(t=>(Object.entries(e).forEach(([e,r])=>{t[e]=r}),t))},async _saveData(e){var t=await ta(this.parent),r=this._data;e.id=this.id;var a=[{previous:r,document:e}];return t.storageInstance.bulkWrite(a,"local-document-save-data").then(t=>{if(t.error[0])throw t.error[0];var r=vt(this.collection.schema.primaryPath,a,t)[0];(e=s(e))._rev=r._rev})},async remove(){var e=await ta(this.parent),t=s(this._data);return t._deleted=!0,ct(e.storageInstance,{previous:this._data,document:t},"local-document-remove").then(t=>e.docCache.getCachedRxDocument(t))}},ma=!1,fa=()=>{if(!ma){ma=!0;var e=Kt;Object.getOwnPropertyNames(e).forEach(t=>{if(!Object.getOwnPropertyDescriptor(da,t)){var r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(da,t,r)}});["populate","update","putAttachment","putAttachmentBase64","getAttachment","allAttachments"].forEach(e=>da[e]=(e=>()=>{throw R("LD6",{functionName:e})})(e))}};function pa(e){var t=e.parent;return t instanceof Kr?t:t.database}function va(e){var t=e.database?e.database:e,r=e.database?e.name:"",a=(async()=>{var a=await ra(t.token,t.storage,t.name,r,t.instanceCreationOptions,t.multiInstance);a=mt(t,a,sa);var n=new ur("id",t.eventBulks$.pipe((0,Nt.p)(e=>{var t=!1;return(""===r&&!e.collectionName||""!==r&&e.collectionName===r)&&(t=!0),t&&e.isLocal}),(0,$t.T)(e=>e.events)),t=>function(e,t){fa();var r=new la(e.id,e,t);return Object.setPrototypeOf(r,da),r.prototype=da,r}(t,e)),i=new Ft(a,"id",()=>{},()=>{}),s=await t.storageToken,o=a.changeStream().subscribe(r=>{for(var a=new Array(r.events.length),n=r.events,i=e.database?e.name:void 0,o=0;o{e.insertLocal=oa,e.upsertLocal=ca,e.getLocal=ua,e.getLocal$=ha},RxDatabase:e=>{e.insertLocal=oa,e.upsertLocal=ca,e.getLocal=ua,e.getLocal$=ha}},hooks:{createRxDatabase:{before:e=>{e.creator.localDocuments&&va(e.database)}},createRxCollection:{before:e=>{e.creator.localDocuments&&va(e.collection)}},preCloseRxDatabase:{after:e=>aa(e)},postCloseRxCollection:{after:e=>aa(e)},postRemoveRxDatabase:{after:e=>na(e.storage,e.databaseName,"")},postRemoveRxCollection:{after:e=>na(e.storage,e.databaseName,e.collectionName)}},overwritable:{}};let ga;function ba(){return"undefined"!=typeof window&&window.indexedDB}function wa(){return ga||(ga=(async()=>{!function(e){if(it("preAddRxPlugin",{plugin:e,plugins:Gr}),!Gr.has(e)){if(Xr.has(e.name))throw R("PL3",{name:e.name,plugin:e});if(Gr.add(e),Xr.add(e.name),!e.rxdb)throw O("PL1",{plugin:e});e.init&&e.init(),e.prototypes&&Object.entries(e.prototypes).forEach(([e,t])=>t(Jr[e])),e.overwritable&&Object.assign(x,e.overwritable),e.hooks&&Object.entries(e.hooks).forEach(([e,t])=>{t.after&&nt[e].push(t.after),t.before&&nt[e].unshift(t.before)})}}(ya);return await Vr({name:"rxdb-landing-v4",localDocuments:!0,storage:Ot()})})()),ga}},8342(e,t,r){r.d(t,{L$:()=>s,Z2:()=>i,zs:()=>n});var a="abcdefghijklmnopqrstuvwxyz";function n(e=10){for(var t="",r=0;rs});n(6540);var o=n(8853),a=n(4848);function s({author:e,year:t,sourceLink:n,children:s}){return(0,a.jsxs)("div",{style:{borderLeft:"2px solid var(--color-top)",paddingLeft:"1rem",paddingTop:"0.5rem",paddingBottom:"0.5rem",marginTop:30,marginBottom:30},children:[(0,a.jsx)(o.iG,{}),(0,a.jsx)("div",{style:{display:"flex",alignItems:"flex-start",gap:"0.5rem"},children:(0,a.jsx)("p",{style:{margin:0},children:s})}),(0,a.jsx)("div",{style:{display:"flex",justifyContent:"flex-end",gap:5},children:(0,a.jsx)(o.fz,{})}),(0,a.jsxs)("p",{style:{marginTop:"0.75rem",marginBottom:0,textAlign:"right",fontSize:"0.9rem"},children:["\u2013"," ",n?(0,a.jsx)("a",{href:n,target:"_blank",rel:"noopener noreferrer",children:e}):e,t&&`, ${t}`]})]})}},3963(e,t,n){n.r(t),n.d(t,{assets:()=>h,contentTitle:()=>l,default:()=>u,frontMatter:()=>r,metadata:()=>o,toc:()=>c});const o=JSON.parse('{"id":"downsides-of-offline-first","title":"Downsides of Local First / Offline First","description":"Discover the hidden pitfalls of local-first apps. Learn about storage limits, conflicts, and real-time illusions before building your offline solution.","source":"@site/docs/downsides-of-offline-first.md","sourceDirName":".","slug":"/downsides-of-offline-first.html","permalink":"/downsides-of-offline-first.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Downsides of Local First / Offline First","slug":"downsides-of-offline-first.html","description":"Discover the hidden pitfalls of local-first apps. Learn about storage limits, conflicts, and real-time illusions before building your offline solution."},"sidebar":"tutorialSidebar","previous":{"title":"Why NoSQL Powers Modern UI Apps","permalink":"/why-nosql.html"},"next":{"title":"RxDB - The Real-Time Database for Node.js","permalink":"/nodejs-database.html"}}');var a=n(4848),s=n(8453),i=n(2443);const r={title:"Downsides of Local First / Offline First",slug:"downsides-of-offline-first.html",description:"Discover the hidden pitfalls of local-first apps. Learn about storage limits, conflicts, and real-time illusions before building your offline solution."},l="Downsides of Local First / Offline First",h={},c=[{value:"It only works with small datasets",id:"it-only-works-with-small-datasets",level:2},{value:"Browser storage is not really persistent",id:"browser-storage-is-not-really-persistent",level:2},{value:"There can be conflicts",id:"there-can-be-conflicts",level:2},{value:"Realtime is a lie",id:"realtime-is-a-lie",level:2},{value:"Eventual consistency",id:"eventual-consistency",level:2},{value:"Permissions and authentication",id:"permissions-and-authentication",level:2},{value:"You have to migrate the client database",id:"you-have-to-migrate-the-client-database",level:2},{value:"Performance is not native",id:"performance-is-not-native",level:2},{value:"Nothing is predictable",id:"nothing-is-predictable",level:2},{value:"There is no relational data",id:"there-is-no-relational-data",level:2}];function d(e){const t={a:"a",blockquote:"blockquote",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.header,{children:(0,a.jsx)(t.h1,{id:"downsides-of-local-first--offline-first",children:"Downsides of Local First / Offline First"})}),"\n",(0,a.jsxs)(t.p,{children:["So you have read ",(0,a.jsx)(t.a,{href:"/offline-first.html",children:"all these things"})," about how the ",(0,a.jsx)(t.a,{href:"/articles/local-first-future.html",children:"local-first"})," (aka offline-first) paradigm makes it easy to create realtime web applications that even work when the user has no internet connection.\nBut there is no free lunch. The offline first paradigm is not the perfect approach for all kinds of apps."]}),"\n",(0,a.jsxs)(i.G,{author:"Daniel",year:"2024",sourceLink:"https://github.com/pubkey",children:["You fully understood a technology when you know when ",(0,a.jsx)("b",{children:"not"})," to use it"]}),"\n",(0,a.jsxs)(t.p,{children:["In the following I will point out the limitations you need to know before you decide to use ",(0,a.jsx)(t.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB"})," or even before you decide to create an offline first application."]}),"\n",(0,a.jsx)(t.h2,{id:"it-only-works-with-small-datasets",children:"It only works with small datasets"}),"\n",(0,a.jsx)(t.p,{children:"Making data available offline means it must be loaded from the server and then stored at the clients device.\nYou need to load the full dataset on the first pageload and on every ongoing load you need to download the new changes to that set.\nWhile in theory you could download in infinite amount of data, in practice you have a limit how long the user can wait before having an up-to-date state.\nYou want to display chat messages like Whatsapp? No problem. Syncing all the messages a user could write, can be done with a few HTTP requests.\nWant to make a tool that displays server logs? Good luck downloading terabytes of data to the client just to search for a single string. This will not work."}),"\n",(0,a.jsxs)(t.p,{children:["Besides the network usage, there is another limit for the size of your data.\nIn browsers you have some options for storage: Cookies, ",(0,a.jsx)(t.a,{href:"/articles/localstorage.html",children:"Localstorage"}),", ",(0,a.jsx)(t.a,{href:"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html#what-was-websql",children:"WebSQL"})," and ",(0,a.jsx)(t.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"}),"."]}),"\n",(0,a.jsxs)(t.p,{children:["Because Cookies and ",(0,a.jsx)(t.a,{href:"/articles/localstorage.html",children:"Localstorage"})," is slow and WebSQL is deprecated, you will use IndexedDB.\nThe ",(0,a.jsx)(t.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"limit of how much data you can store in IndexedDB"})," depends on two factors: Which browser is used and how much disc space is left on the device. You can assume that at least a couple of ",(0,a.jsx)(t.a,{href:"https://web.dev/storage-for-the-web/",children:"hundred megabytes"})," are available at least. The maximum is potentially hundreds of gigabytes or more, but the browser implementations vary. Chrome allows the browser to use up to 60% of the total disc space per origin. Firefox allows up to 50%. But on safari you can only store up to 1GB and the browser will prompt the user on each additional 200MB increment."]}),"\n",(0,a.jsx)(t.p,{children:"The problem is, that you have no chance to really predict how much data can be stored. So you have to make assumptions that are hopefully true for all of your users. Also, you have no way to increase that space like you would add another hard drive to your backend server. Once your clients reach the limit, you likely have to rewrite big parts of your applications."}),"\n",(0,a.jsxs)(t.p,{children:["UPDATE (2023): Newer versions of browsers can store way more data, for example firefox stores up to 10% of the total disk size. For an overview about how much can be stored, read ",(0,a.jsx)(t.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria",children:"this guide"})]}),"\n",(0,a.jsx)(t.h2,{id:"browser-storage-is-not-really-persistent",children:"Browser storage is not really persistent"}),"\n",(0,a.jsxs)(t.p,{children:["When data is stored inside IndexedDB or one of the other storage APIs, it cannot be trusted to stay there forever.\nApple for example deletes the data when the website was not used in the ",(0,a.jsx)(t.a,{href:"https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/",children:"last 7 days"}),". The other browsers also have logic to clean up the stored data, and in the end the user itself could be the one that deletes the browsers local data."]}),"\n",(0,a.jsxs)(t.p,{children:["The most common way to handle this, is to replicate everything from the backend to the client again.\nOf course, this does not work for state that is not stored at the backend. So if you assume you can store the users private data inside the browser in a secure way, you are ",(0,a.jsx)(t.a,{href:"https://medium.com/universal-ethereum/out-of-gas-were-shutting-down-unilogin-3b544838df1a#4f60",children:"wrong"}),"."]}),"\n",(0,a.jsx)("p",{align:"center",children:(0,a.jsx)("img",{src:"./files/safari-database.png",alt:"safari database",width:"200"})}),"\n",(0,a.jsx)(t.h2,{id:"there-can-be-conflicts",children:"There can be conflicts"}),"\n",(0,a.jsxs)(t.p,{children:["Imagine two of your users modify the same JSON document, while both are offline. After they go online again, their clients replicate the modified document to the server. Now you have two conflicting versions of the same document, and you need a way to determine how the correct new version of that document should look like. This process is called ",(0,a.jsx)(t.strong,{children:"conflict resolution"}),"."]}),"\n",(0,a.jsx)("p",{align:"center",children:(0,a.jsx)("img",{src:"./files/document-replication-conflict.svg",alt:"document replication conflict",width:"250"})}),"\n",(0,a.jsxs)(t.ol,{children:["\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsxs)(t.p,{children:["The default in ",(0,a.jsx)(t.a,{href:"https://docs.couchdb.org/en/stable/replication/conflicts.html",children:"many"})," offline first databases is a deterministic conflict resolution strategy. Both conflicting versions of the document are kept in the storage and when you query for the document, a winner is determined by comparing the hashes of the document and only the winning document is returned. Because the comparison is deterministic, all clients and servers will always pick the same winner. This kind of resolution only works when it is not that important that one of the document changes gets dropped. Because conflicts are rare, this might be a viable solution for some use cases."]}),"\n"]}),"\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsx)(t.p,{children:"A better resolution can be applied by listening to the changestream of the database. The changestream emits an event each time a write happens to the database. The event contains information about the written document and also a flag if there is a conflicting version. For each event with a conflict, you fetch all versions for that document and create a new document that contains the winning state. With that you can implement pretty complex conflict resolution strategies, but you have to manually code it for each collection of documents."}),"\n"]}),"\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsxs)(t.p,{children:["Instead of the solving conflict once at every client, it can be made a bit easier by solely relying on the backend. This can be done when all of your clients replicate with the same single backend server. With ",(0,a.jsx)(t.a,{href:"/replication-graphql.html",children:"RxDB's Graphql Replication"})," each client side change is sent to the server where conflicts can be resolved and the winning document can be sent back to the clients."]}),"\n"]}),"\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsx)(t.p,{children:"Sometimes there is no way to solve a conflict with code. If your users edit text based documents or images, often only the users themselves can decide how the winning revision has to look. For these cases, you have to implement complex UI parts where the users can inspect the conflict and manage its resolution."}),"\n"]}),"\n",(0,a.jsxs)(t.li,{children:["\n",(0,a.jsxs)(t.p,{children:["You do not have to handle conflicts if they cannot happen in the first place. You can achieve that by designing a write only database where existing documents cannot be touched. Instead of storing the current state in a single document, you store all the events that lead to the current state. Sometimes called the ",(0,a.jsx)(t.a,{href:"https://pouchdb.com/guides/conflicts.html#accountants-dont-use-erasers",children:'"everything is a delta"'})," strategy, others would call it ",(0,a.jsx)(t.a,{href:"https://martinfowler.com/eaaDev/EventSourcing.html",children:"Event Sourcing"}),". Like an accountant that does not need an eraser, you append all changes and afterwards aggregate the current state at the client."]}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(t.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(t.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(t.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(t.span,{"data-line":"",children:(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-comment)"},children:"// create one new document for each change to the users balance"})}),"\n",(0,a.jsxs)(t.span,{"data-line":"",children:[(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"{id"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-function)"},children:".toJSON"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:" change"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-comment)"},children:"// balance increased by $100"})]}),"\n",(0,a.jsxs)(t.span,{"data-line":"",children:[(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"{id"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-function)"},children:".toJSON"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:" change"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-keyword)"},children:" -"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-constant)"},children:"50"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-comment)"},children:"// balance decreased by $50"})]}),"\n",(0,a.jsxs)(t.span,{"data-line":"",children:[(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"{id"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-function)"},children:".toJSON"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:" change"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-constant)"},children:" 200"}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(t.span,{style:{color:"var(--shiki-token-comment)"},children:"// balance increased by $200"})]})]})})}),"\n",(0,a.jsxs)(t.ol,{start:"6",children:["\n",(0,a.jsxs)(t.li,{children:["There is this thing called ",(0,a.jsx)(t.strong,{children:"conflict-free replicated data type"}),", short ",(0,a.jsx)(t.strong,{children:"CRDT"}),". Using a CRDT library like ",(0,a.jsx)(t.a,{href:"https://github.com/automerge/automerge",children:"automerge"})," will magically solve all of your conflict problems. Until you use it in production where you observe that implementing CRDTs has basically the same complexity as implementing conflict resolution strategies."]}),"\n"]}),"\n",(0,a.jsx)(t.h2,{id:"realtime-is-a-lie",children:"Realtime is a lie"}),"\n",(0,a.jsxs)(t.p,{children:["So you replicate stuff between the clients and your backend. Each change on one side directly changes the state of the other sides in ",(0,a.jsx)(t.strong,{children:"realtime"}),'. But this "realtime" is not the same as in ',(0,a.jsx)(t.a,{href:"https://en.wikipedia.org/wiki/Real-time_computing",children:"realtime computing"}),". In the offline first world, the word realtime was introduced by firebase and is more meant as a marketing slogan than a technical description.\nThere is an internet between your backend and your clients and everything you do on one machine takes at least once the latency until it can affect anything on the other machines. You have to keep this in mind when you develop anything where the timing is important, like a multiplayer game or a stock trading app."]}),"\n",(0,a.jsx)(t.p,{children:'Even when you run a query against the local database, there is no "real" realtime.\nClient side databases run on JavaScript and JavaScript runs on a single CPU that might be partially blocked because the user is running some background processes. So you can never guarantee a response deadline which violates the time constraint of realtime computing.'}),"\n",(0,a.jsx)("p",{align:"center",children:(0,a.jsx)("img",{src:"./files/latency-london-san-franzisco.png",alt:"latency london san franzisco",width:"300"})}),"\n",(0,a.jsx)(t.h2,{id:"eventual-consistency",children:"Eventual consistency"}),"\n",(0,a.jsx)(t.p,{children:"An offline first app does not have a single source of truth. There is a source on the backend, one on the own client, and also each other client has its own definition of truth. At the moment your user starts the app, the local state is hopefully already replicated with the backend and all other clients. But this does not have to be true, the states can have converged and you have to plan for that.\nThe user could update a document based on wrong assumptions because it was not fully replicated at that point in time because the user is offline. A good way to handle this problem is to show the replication state in the UI and tell the user when the replication is running, stopped, paused or finished."}),"\n",(0,a.jsx)(t.p,{children:'And some data is just too important to be "eventual consistent". Create a wire transfer in your online banking app while you are offline. You keep the smartphone laying at your night desk and when you use again in the next morning, it goes online and replicates the transaction. No thank you, do not use offline first for these kinds of things, or at least you have to display the replication state of each document in the UI.'}),"\n",(0,a.jsx)("p",{align:"center",children:(0,a.jsx)("img",{src:"./files/cap-theorem.png",alt:"CAP theorem",width:"150"})}),"\n",(0,a.jsx)(t.h2,{id:"permissions-and-authentication",children:"Permissions and authentication"}),"\n",(0,a.jsx)(t.p,{children:"Every offline first app that goes beyond a prototype, does likely not have the same global state for all of its users. Each user has a different set of documents that are allowed to be replicated or seen by the user. So you need some kind of authentication and permission handling to divide the documents.\nThe easy way is to just create one database for each user on the backend and only allow to replicate that one. Creating that many databases is not really a problem with for example CouchDB, and it makes permission handling easy.\nBut as soon as you want to query all of your data in the backend, it will bite back. Your data is not at a single place, it is distributed between all of the user specific databases. This becomes even more complex as soon as you store information together with the documents that is not allowed to be seen by outsiders. You not only have to decide which documents to replicate, but also which fields of them."}),"\n",(0,a.jsxs)(t.p,{children:["So what you really want is a single datastore in the backend and then replicate only the allowed document parts to each of the users.\nThis always requires you to implement your custom replication endpoint like what you do with RxDBs ",(0,a.jsx)(t.a,{href:"/replication-graphql.html",children:"GraphQL Replication"}),"."]}),"\n",(0,a.jsx)(t.h2,{id:"you-have-to-migrate-the-client-database",children:"You have to migrate the client database"}),"\n",(0,a.jsx)(t.p,{children:"While developing your app, sooner or later you want to change the data layout. You want to add some new fields to documents or change the format of them. So you have to update the database schema and also migrate the stored documents.\nWith 'normal' applications, this is already hard enough and often dangerous. You wait until midnight, stop the webserver, make a database backup, deploy the new schema and then you hope that nothing goes wrong while it updates that many documents."}),"\n",(0,a.jsxs)(t.p,{children:["With offline first applications, it is even more fun. You do not only have to migrate your local backend database, you also have to provide a ",(0,a.jsx)(t.a,{href:"/migration-schema.html",children:"migration strategy"})," for all of these client databases out there. And you also cannot migrate everything at the same time. The clients can only migrate when the new code was updated from the appstore or the user visited your website again. This could be today or in a few weeks."]}),"\n",(0,a.jsx)(t.h2,{id:"performance-is-not-native",children:"Performance is not native"}),"\n",(0,a.jsxs)(t.p,{children:["When you create a web based offline first app, you cannot store data directly on the users filesystem. In fact there are many layers between your JavaScript code and the filesystem of the operation system. Let's say you insert a document in ",(0,a.jsx)(t.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB"}),":"]}),"\n",(0,a.jsxs)(t.ul,{children:["\n",(0,a.jsx)(t.li,{children:"You call the RxDB API to validate and store the data"}),"\n",(0,a.jsx)(t.li,{children:"RxDB calls the underlying RxStorage, for example PouchDB."}),"\n",(0,a.jsx)(t.li,{children:"Pouchdb calls its underlying storage adapter"}),"\n",(0,a.jsx)(t.li,{children:"The storage adapter calls IndexedDB"}),"\n",(0,a.jsx)(t.li,{children:"The browser runs its internal handling of the IndexedDB API"}),"\n",(0,a.jsxs)(t.li,{children:["In most browsers IndexedDB is implemented on ",(0,a.jsx)(t.a,{href:"https://hackaday.com/2021/08/24/sqlite-on-the-web-absurd-sql/",children:"top of SQLite"})]}),"\n",(0,a.jsx)(t.li,{children:"SQLite calls the OS to store the data in the filesystem"}),"\n"]}),"\n",(0,a.jsx)(t.p,{children:"All these layers are abstractions. They are not build for exactly that one use case, so you lose some performance to tunnel the data through the layer itself, and you also lose some performance because the abstraction does not exactly provide the functions that are needed by the layer above and it will overfetch data."}),"\n",(0,a.jsx)(t.p,{children:"You will not find a benchmark comparison between how many transactions per second you can run on the browser compared to a server based database. Because it makes no sense to compare them. Browsers are slower, JavaScript is slower."}),"\n",(0,a.jsxs)(t.blockquote,{children:["\n",(0,a.jsx)(t.p,{children:(0,a.jsx)(t.strong,{children:"Is it fast enough?"})}),"\n"]}),"\n",(0,a.jsxs)(t.p,{children:['What you really care about is "Is it fast enough?". For most use cases, the answer is ',(0,a.jsx)(t.code,{children:"yes"}),'. Offline first apps are UI based and you do not need to process a million transactions per second, because your user will not click the save button that often. "Fast enough" means that the data is processed in under 16 milliseconds so that you can render the updated UI in the next frame. This is of course not true for all use cases, so you better think about the performance limit before starting with the implementation.']}),"\n",(0,a.jsx)(t.h2,{id:"nothing-is-predictable",children:"Nothing is predictable"}),"\n",(0,a.jsx)(t.p,{children:"You have a PostgreSQL database and run a query over 1000ths of rows, which takes 200 milliseconds. Works great, so you now want to do something similar at the client device in your offline first app. How long does it take? You cannot know because people have different devices, and even equal devices have different things running in the background that slow the CPUs. So you cannot predict performance and as described above, you cannot even predict the storage limit. So if your app does heavy data analytics, you might better run everything on the backend and just send the results to the client."}),"\n",(0,a.jsx)(t.h2,{id:"there-is-no-relational-data",children:"There is no relational data"}),"\n",(0,a.jsxs)(t.p,{children:["I started creating ",(0,a.jsx)(t.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB"})," many years ago and while still maintaining it, I often worked with all these other offline first databases out there. RxDB and all of these other ones, are based on some kind of document databases similar to NoSQL. Often people want to have a relational database like the SQL one they use at the backend."]}),"\n",(0,a.jsxs)(t.p,{children:["So why are there no real relations in offline first databases? I could answer with these arguments like how JavaScript works better with document based data, how performance is better when having no joins or even how NoSQL queries are more composable. But the truth is, everything is NoSQL because it makes replication easy. An SQL query that mutates data in different tables based on some selects and joins, cannot be partially replicated without breaking the client. You have foreign keys that point to other rows and if these rows are not replicated yet, you have a problem. To implement a robust ",(0,a.jsx)(t.a,{href:"/replication.html",children:"Sync Engine"})," for relational data, you need some stuff like a ",(0,a.jsx)(t.a,{href:"https://www.theverge.com/2012/11/26/3692392/google-spanner-atomic-clocks-GPS",children:"reliable atomic clock"})," and you have to block queries over multiple tables while a transaction replicated. ",(0,a.jsx)(t.a,{href:"https://youtu.be/iEFcmfmdh2w?t=607",children:"Watch this guy"})," implementing offline first replication on top of SQLite or read this ",(0,a.jsx)(t.a,{href:"https://github.com/supabase/supabase/discussions/357",children:"discussion"})," about implementing ",(0,a.jsx)(t.a,{href:"/replication-supabase.html",children:"offline first in supabase"}),"."]}),"\n",(0,a.jsx)(t.p,{children:"So creating replication for an SQL offline first database is way more work than just adding some network protocols on top of PostgreSQL. It might not even be possible for clients that have no reliable clock."}),"\n",(0,a.jsx)("p",{align:"center",children:(0,a.jsx)("img",{src:"./files/no-relational-data.png",alt:"no relational data",width:"250"})})]})}function u(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453(e,t,n){n.d(t,{R:()=>i,x:()=>r});var o=n(6540);const a={},s=o.createContext(a);function i(e){const t=o.useContext(s);return o.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),o.createElement(s.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/6ae3580c.67a503bd.js b/docs/assets/js/6ae3580c.67a503bd.js
deleted file mode 100644
index 0c540bf1739..00000000000
--- a/docs/assets/js/6ae3580c.67a503bd.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5320],{2325(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>t,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"replication","title":"\u2699\ufe0f RxDB realtime Sync Engine for Local-First Apps","description":"Replicate data in real-time with RxDB\'s offline-first Sync Engine. Learn about efficient syncing, conflict resolution, and advanced multi-tab support.","source":"@site/docs/replication.md","sourceDirName":".","slug":"/replication.html","permalink":"/replication.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"\u2699\ufe0f RxDB realtime Sync Engine for Local-First Apps","slug":"replication.html","description":"Replicate data in real-time with RxDB\'s offline-first Sync Engine. Learn about efficient syncing, conflict resolution, and advanced multi-tab support."},"sidebar":"tutorialSidebar","previous":{"title":"Electron","permalink":"/electron.html"},"next":{"title":"HTTP Replication","permalink":"/replication-http.html"}}');var r=s(4848),o=s(8453);const t={title:"\u2699\ufe0f RxDB realtime Sync Engine for Local-First Apps",slug:"replication.html",description:"Replicate data in real-time with RxDB's offline-first Sync Engine. Learn about efficient syncing, conflict resolution, and advanced multi-tab support."},a="RxDB's realtime Sync Engine for Local-First Apps",l={},c=[{value:"Design Decisions of the Sync Engine",id:"design-decisions-of-the-sync-engine",level:2},{value:"The Sync Engine on the document level",id:"the-sync-engine-on-the-document-level",level:2},{value:"The Sync Engine on the transfer level",id:"the-sync-engine-on-the-transfer-level",level:2},{value:"Checkpoint iteration",id:"checkpoint-iteration",level:3},{value:"Event observation",id:"event-observation",level:3},{value:"Data layout on the server",id:"data-layout-on-the-server",level:2},{value:"Conflict handling",id:"conflict-handling",level:2},{value:"replicateRxCollection()",id:"replicaterxcollection",level:2},{value:"Multi Tab support",id:"multi-tab-support",level:2},{value:"Error handling",id:"error-handling",level:2},{value:"Security",id:"security",level:2},{value:"RxReplicationState",id:"rxreplicationstate",level:2},{value:"Observable",id:"observable",level:3},{value:"awaitInitialReplication()",id:"awaitinitialreplication",level:3},{value:"awaitInSync()",id:"awaitinsync",level:3},{value:"awaitInitialReplication() and awaitInSync() should not be used to block the application",id:"awaitinitialreplication-and-awaitinsync-should-not-be-used-to-block-the-application",level:4},{value:"reSync()",id:"resync",level:3},{value:"cancel()",id:"cancel",level:3},{value:"pause()",id:"pause",level:3},{value:"remove()",id:"remove",level:3},{value:"isStopped()",id:"isstopped",level:3},{value:"isPaused()",id:"ispaused",level:3},{value:"Setting a custom initialCheckpoint",id:"setting-a-custom-initialcheckpoint",level:3},{value:"toggleOnDocumentVisible",id:"toggleondocumentvisible",level:3},{value:"Attachment replication",id:"attachment-replication",level:2},{value:"Pull-Only Replication",id:"pull-only-replication",level:2},{value:"Partial Sync with RxDB",id:"partial-sync-with-rxdb",level:2},{value:"Idea: One Collection, Multiple Replications",id:"idea-one-collection-multiple-replications",level:3},{value:"Diffy-Sync when Revisiting a Chunk",id:"diffy-sync-when-revisiting-a-chunk",level:3},{value:"Partial Sync in a Local-First Business Application",id:"partial-sync-in-a-local-first-business-application",level:3},{value:"FAQ",id:"faq",level:2}];function h(e){const n={a:"a",admonition:"admonition",br:"br",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",mdxAdmonitionTitle:"mdxAdmonitionTitle",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components},{Details:s}=n;return s||function(e,n){throw new Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"rxdbs-realtime-sync-engine-for-local-first-apps",children:"RxDB's realtime Sync Engine for Local-First Apps"})}),"\n",(0,r.jsxs)(n.p,{children:["The RxDB Sync Engine provides the ability to sync the database state in ",(0,r.jsx)(n.strong,{children:"realtime"})," between the clients and the server."]}),"\n",(0,r.jsxs)(n.p,{children:["The backend server does not have to be a RxDB instance; you can build a replication with ",(0,r.jsx)(n.strong,{children:"any infrastructure"}),".\nFor example you can replicate with a ",(0,r.jsx)(n.a,{href:"/replication-graphql.html",children:"custom GraphQL endpoint"})," or a ",(0,r.jsx)(n.a,{href:"/replication-http.html",children:"HTTP server"})," on top of a PostgreSQL or MongoDB database."]}),"\n",(0,r.jsxs)(n.p,{children:["The replication is made to support the ",(0,r.jsx)(n.a,{href:"/articles/local-first-future.html",children:"Local-First"})," paradigm, so that when the client goes ",(0,r.jsx)(n.a,{href:"/offline-first.html",children:"offline"}),", the RxDB ",(0,r.jsx)(n.a,{href:"/rx-database.html",children:"database"})," can still read and write ",(0,r.jsx)(n.a,{href:"/articles/local-database.html",children:"locally"})," and will continue the replication when the client goes online again."]}),"\n",(0,r.jsx)(n.h2,{id:"design-decisions-of-the-sync-engine",children:"Design Decisions of the Sync Engine"}),"\n",(0,r.jsx)(n.p,{children:"In contrast to other (server-side) database replication protocols, the RxDB Sync Engine was designed with these goals in mind:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Easy to Understand"}),': The sync engine works in a simple "git-like" way that is easy to understand for an average developer. You only have to understand how three simple endpoints work.']}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Complex Parts are in RxDB, not in the Backend"}),": The complex parts of the Sync Engine, like ",(0,r.jsx)(n.a,{href:"/transactions-conflicts-revisions.html",children:"conflict handling"})," or offline-online switches, are implemented inside of RxDB itself. This makes creating a compatible backend very easy."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Compatible with any Backend"}),': Because the complex parts are in RxDB, the backend can be "dump" which makes the protocol compatible to almost every backend. No matter if you use PostgreSQL, MongoDB or anything else.']}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Performance is optimized for Client Devices and Browsers"}),": By grouping updates and fetches into batches, it is faster to transfer and easier to compress. Client devices and browsers can also process this data faster, for example running ",(0,r.jsx)(n.code,{children:"JSON.parse()"})," on a chunk of data is faster than calling it once per row. Same goes for how client side storage like ",(0,r.jsx)(n.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," or ",(0,r.jsx)(n.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," works where writing data in bulks is faster."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Offline-First Support"}),": By incorporating conflict handling at the client side, the protocol fully supports ",(0,r.jsx)(n.a,{href:"/offline-first.html",children:"offline-first apps"}),". Users can continue making changes while offline, and those updates will sync seamlessly once a connection is reestablished - all without risking data loss or having undefined behavior."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Multi-Tab Support"}),": When RxDB is used in a browser and multiple tabs of the same application are opened, only exactly one runs the replication at any given time. This reduces client- and backend resources."]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"the-sync-engine-on-the-document-level",children:"The Sync Engine on the document level"}),"\n",(0,r.jsx)(n.p,{children:"On the RxDocument level, the replication works like git, where the fork/client contains all new writes and must be merged with the master/server before it can push its new state to the master/server."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"A---B-----------D master/server state\n \\ /\n B---C---D fork/client state\n"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["The client pulls the latest state ",(0,r.jsx)(n.code,{children:"B"})," from the master."]}),"\n",(0,r.jsxs)(n.li,{children:["The client does some changes ",(0,r.jsx)(n.code,{children:"C+D"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:["The client pushes these changes to the master by sending the latest known master state ",(0,r.jsx)(n.code,{children:"B"})," and the new client state ",(0,r.jsx)(n.code,{children:"D"})," of the document."]}),"\n",(0,r.jsxs)(n.li,{children:["If the master state is equal to the latest master ",(0,r.jsx)(n.code,{children:"B"})," state of the client, the new client state ",(0,r.jsx)(n.code,{children:"D"})," is set as the latest master state."]}),"\n",(0,r.jsx)(n.li,{children:"If the master also had changes and so the latest master change is different then the one that the client assumes, we have a conflict that has to be resolved on the client."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"the-sync-engine-on-the-transfer-level",children:"The Sync Engine on the transfer level"}),"\n",(0,r.jsxs)(n.p,{children:["When document states are transferred, all handlers use batches of documents for better performance.\nThe server ",(0,r.jsx)(n.strong,{children:"must"})," implement the following methods to be compatible with the replication:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"pullHandler"})," Get the last checkpoint (or null) as input. Returns all documents that have been written ",(0,r.jsx)(n.strong,{children:"after"})," the given checkpoint. Also returns the checkpoint of the latest written returned document."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"pushHandler"})," a method that can be called by the client to send client side writes to the master. It gets an array with the ",(0,r.jsx)(n.code,{children:"assumedMasterState"})," and the ",(0,r.jsx)(n.code,{children:"newForkState"})," of each document write as input. It must return an array that contains the master document states of all conflicts. If there are no conflicts, it must return an empty array."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"pullStream"})," an observable that emits batches of all master writes and the latest checkpoint of the write batches."]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:" +--------+ +--------+ \n | | pullHandler() | |\n | |---------------------\x3e | | \n | | | | \n | | | |\n | Client | pushHandler() | Server |\n | |---------------------\x3e | | \n | | | |\n | | pullStream$ | | \n | | <-------------------------| | \n +--------+ +--------+\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The replication runs in two ",(0,r.jsx)(n.strong,{children:"different modes"}),":"]}),"\n",(0,r.jsx)(n.h3,{id:"checkpoint-iteration",children:"Checkpoint iteration"}),"\n",(0,r.jsxs)(n.p,{children:["On first initial replication, or when the client comes online again, a checkpoint based iteration is used to catch up with the server state.\nA checkpoint is a subset of the fields of the last pulled document. When the checkpoint is send to the backend via ",(0,r.jsx)(n.code,{children:"pullHandler()"}),", the backend must be able to respond with all documents that have been written ",(0,r.jsx)(n.strong,{children:"after"})," the given checkpoint.\nFor example if your documents contain an ",(0,r.jsx)(n.code,{children:"id"})," and an ",(0,r.jsx)(n.code,{children:"updatedAt"})," field, these two can be used as checkpoint."]}),"\n",(0,r.jsxs)(n.p,{children:["When the checkpoint iteration reaches the last checkpoint, where the backend returns an empty array because there are no newer documents, the replication will automatically switch to the ",(0,r.jsx)(n.code,{children:"event observation"})," mode."]}),"\n",(0,r.jsx)(n.h3,{id:"event-observation",children:"Event observation"}),"\n",(0,r.jsxs)(n.p,{children:["While the client is connected to the backend, the events from the backend are observed via ",(0,r.jsx)(n.code,{children:"pullStream$"})," and persisted to the client."]}),"\n",(0,r.jsxs)(n.p,{children:["If your backend for any reason is not able to provide a full ",(0,r.jsx)(n.code,{children:"pullStream$"})," that contains all events and the checkpoint, you can instead only emit ",(0,r.jsx)(n.code,{children:"RESYNC"})," events that tell RxDB that anything unknown has changed on the server and it should run the pull replication via ",(0,r.jsx)(n.a,{href:"#checkpoint-iteration",children:"checkpoint iteration"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["When the client goes offline and online again, it might happen that the ",(0,r.jsx)(n.code,{children:"pullStream$"})," has missed out some events. Therefore the ",(0,r.jsx)(n.code,{children:"pullStream$"})," should also emit a ",(0,r.jsx)(n.code,{children:"RESYNC"})," event each time the client reconnects, so that the client can become in sync with the backend via the ",(0,r.jsx)(n.a,{href:"#checkpoint-iteration",children:"checkpoint iteration"})," mode."]}),"\n",(0,r.jsx)(n.h2,{id:"data-layout-on-the-server",children:"Data layout on the server"}),"\n",(0,r.jsx)(n.p,{children:"To use the replication you first have to ensure that:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"documents are deterministic sortable by their last write time"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.em,{children:"deterministic"})," means that even if two documents have the same ",(0,r.jsx)(n.em,{children:"last write time"}),", they have a predictable sort order.\nThis is most often ensured by using the ",(0,r.jsx)(n.em,{children:"primaryKey"})," as second sort parameter as part of the checkpoint."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:(0,r.jsxs)(n.strong,{children:["documents are never deleted, instead the ",(0,r.jsx)(n.code,{children:"_deleted"})," field is set to ",(0,r.jsx)(n.code,{children:"true"}),"."]})}),"\n",(0,r.jsx)(n.p,{children:"This is needed so that the deletion state of a document exists in the database and can be replicated with other instances. If your backend uses a different field to mark deleted documents, you have to transform the data in the push/pull handlers or with the modifiers."}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"For example if your documents look like this:"}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docData"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "id"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "foobar"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "name"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Alice"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "lastName"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Wilson"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Contains the last write timestamp"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * so all documents writes can be sorted by that value"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * when they are fetched from the remote instance."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "updatedAt"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1564483474"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Instead of physically deleting documents,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * a deleted document gets replicated."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "_deleted"'}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,r.jsxs)(n.p,{children:["Then your data is always sortable by ",(0,r.jsx)(n.code,{children:"updatedAt"}),". This ensures that when RxDB fetches 'new' changes via ",(0,r.jsx)(n.code,{children:"pullHandler()"}),", it can send the latest ",(0,r.jsx)(n.code,{children:"updatedAt+id"})," checkpoint to the remote endpoint and then receive all newer documents."]}),"\n",(0,r.jsxs)(n.p,{children:["By default, the field is ",(0,r.jsx)(n.code,{children:"_deleted"}),". If your remote endpoint uses a different field to mark deleted documents, you can set the ",(0,r.jsx)(n.code,{children:"deletedField"})," in the replication options which will automatically map the field on all pull and push requests."]}),"\n",(0,r.jsx)(n.h2,{id:"conflict-handling",children:"Conflict handling"}),"\n",(0,r.jsx)(n.p,{children:"When multiple clients (or the server) modify the same document at the same time (or when they are offline), it can happen that a conflict arises during the replication."}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"A---B1---C1---X master/server state\n \\ /\n B1---C2 fork/client state\n"})}),"\n",(0,r.jsxs)(n.p,{children:["In the case above, the client would tell the master to move the document state from ",(0,r.jsx)(n.code,{children:"B1"})," to ",(0,r.jsx)(n.code,{children:"C2"})," by calling ",(0,r.jsx)(n.code,{children:"pushHandler()"}),". But because the actual master state is ",(0,r.jsx)(n.code,{children:"C1"})," and not ",(0,r.jsx)(n.code,{children:"B1"}),", the master would reject the write by sending back the actual master state ",(0,r.jsx)(n.code,{children:"C1"}),".\n",(0,r.jsx)(n.strong,{children:"RxDB resolves all conflicts on the client"})," so it would call the conflict handler of the ",(0,r.jsx)(n.code,{children:"RxCollection"})," and create a new document state ",(0,r.jsx)(n.code,{children:"D"})," that can then be written to the master."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"A---B1---C1---X---D master/server state\n \\ / \\ /\n B1---C2---D fork/client state\n"})}),"\n",(0,r.jsxs)(n.p,{children:["The default conflict handler will always drop the fork state and use the master state. This ensures that clients that are offline for a very long time, do not accidentally overwrite other peoples changes when they go online again.\nYou can specify a custom conflict handler by setting the property ",(0,r.jsx)(n.code,{children:"conflictHandler"})," when calling ",(0,r.jsx)(n.code,{children:"addCollection()"}),"."]}),"\n",(0,r.jsxs)(n.p,{children:["Learn how to create a ",(0,r.jsx)(n.a,{href:"/transactions-conflicts-revisions.html#custom-conflict-handler",children:"custom conflict handler"}),"."]}),"\n",(0,r.jsx)(n.h2,{id:"replicaterxcollection",children:"replicateRxCollection()"}),"\n",(0,r.jsxs)(n.p,{children:["You can start the replication of a single ",(0,r.jsx)(n.code,{children:"RxCollection"})," by calling ",(0,r.jsx)(n.code,{children:"replicateRxCollection()"})," like in the following:"]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateRxCollection } "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastOfArray"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * An id for the replication to identify it"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * and so that RxDB is able to resume the replication on app reload."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If you replicate with a remote server, it is recommended to put the"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * server url into the replicationIdentifier."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-rest-replication-to-https://example.com/api/sync'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * By default it will do an ongoing realtime replication."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * By settings live: false the replication will run once until the local state"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * is in sync with the remote state, then it will cancel itself."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional), default is true."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Time in milliseconds after when a failed backend request"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * has to be retried."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This time will be skipped if a offline->online switch is detected"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * via navigator.onLine"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional), default is 5 seconds."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" retryTime"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * When multiInstance is true, like when you use RxDB in multiple browser tabs,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * the replication should always run in only one of the open browser tabs."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If waitForLeadership is true, it will wait until the current instance is leader."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If waitForLeadership is false, it will start replicating, even if it is not leader."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=true]"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" waitForLeadership"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If this is set to false,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * the replication will not start automatically"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * but will wait for replicationState.start() being called."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional), default is true"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" autoStart"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Custom deleted field, the boolean property of the document data that"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * marks a document as being deleted."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If your backend uses a different fieldname then '_deleted', set the fieldname here."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * RxDB will still store the documents internally with '_deleted', setting this field"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * only maps the data on the data layer."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If a custom deleted field contains a non-boolean value, the deleted state"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * of the documents depends on if the value is truthy or not. So instead of providing a boolean * * deleted value, you could also work with using a 'deletedAt' timestamp instead."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default='_deleted']"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" deletedField"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'deleted'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Optional,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * only needed when you want to replicate local changes to the remote instance."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Push handler"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(docs) {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Push the local documents to a remote REST server."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" rawResponse"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'https://example.com/api/sync/push'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" method"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'POST'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Accept'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Content-Type'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" body"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({ docs })"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Contains an array with all conflicts that appeared during this push."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If there were no conflicts, return an empty array."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" rawResponse"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" response;"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Batch size, optional"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Defines how many documents will be given to the push handler at once."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Modifies all documents before they are given to the push handler."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Can be used to swap out a custom deleted flag instead of the '_deleted' field."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If the push modifier return null, the document will be skipped and not send to the remote."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Notice that the modifier can be called multiple times and should not contain any side effects."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" modifier"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" d "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" d"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Optional,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * only needed when you want to replicate remote changes to the local state."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Pull handler"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(lastCheckpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" minTimestamp"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastCheckpoint "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"?"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" lastCheckpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * In this example we replicate with a remote REST server"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `https://example.com/api/sync/?minUpdatedAt="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"minTimestamp"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"&limit="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"batchSize"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" documentsFromRemote"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Contains the pulled documents from the remote."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Not that if documentsFromRemote.length < batchSize,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * then RxDB assumes that there are no more un-replicated documents"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * on the backend, so the replication will switch to 'Event observation' mode."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" documents"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" documentsFromRemote"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The last checkpoint of the returned documents."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * On the next call to the pull handler,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * this checkpoint will be passed as 'lastCheckpoint'"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" documentsFromRemote"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ==="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ?"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastCheckpoint "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" lastOfArray"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documentsFromRemote).id"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" lastOfArray"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(documentsFromRemote).updatedAt"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Modifies all documents after they have been pulled"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * but before they are used by RxDB."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Notice that the modifier can be called multiple times and should not contain any side effects."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" modifier"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" d "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" d"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Stream of the backend document writes."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * See below."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * You only need a stream$ when you have set live=true"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" stream$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pullStream$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".asObservable"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Creating the pull stream for realtime replication."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here we use a websocket but any other way of sending data to the client can be used,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * like long polling or server-sent events."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pullStream$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" Subject"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"RxReplicationPullStreamItem"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"any"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:">>();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" firstOpen "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" connectSocket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" WebSocket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'wss://example.com/api/sync/stream'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * When the backend sends a new batch of documents+checkpoint,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * emit it into the stream$."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * event.data must look like this"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * {"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * documents: ["})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * {"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * id: 'foobar',"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * _deleted: false,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * updatedAt: 1234"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * ],"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * checkpoint: {"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * id: 'foobar',"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * updatedAt: 1234"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onmessage"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" event "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pullStream$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".next"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"event"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".data);"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Automatically reconnect the socket on close and error."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onclose"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" connectSocket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onerror"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".close"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" socket"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onopen"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(firstOpen) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" firstOpen "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"else"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * When the client is offline and goes online again,"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * it might have missed out events that happened on the server."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * So we have to emit a RESYNC so that the replication goes"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * into 'Checkpoint iteration' mode until the client is in sync"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * and then it will go back into 'Event observation' mode again."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pullStream$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".next"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'RESYNC'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "})]})})}),"\n",(0,r.jsx)(n.h2,{id:"multi-tab-support",children:"Multi Tab support"}),"\n",(0,r.jsxs)(n.p,{children:["For better performance, the replication runs only in one instance when RxDB is used in multiple browser tabs or Node.js processes.\nBy setting ",(0,r.jsx)(n.code,{children:"waitForLeadership: false"})," you can enforce that each tab runs its own replication cycles.\nIf used in a multi instance setting, so when at database creation ",(0,r.jsx)(n.code,{children:"multiInstance: false"})," was not set,\nyou need to import the ",(0,r.jsx)(n.a,{href:"/leader-election.html",children:"leader election plugin"})," so that RxDB can know how many instances exist and which browser tab should run the replication."]}),"\n",(0,r.jsx)(n.h2,{id:"error-handling",children:"Error handling"}),"\n",(0,r.jsxs)(n.p,{children:["When sending a document to the remote fails for any reason, RxDB will send it again in a later point in time.\nThis happens for ",(0,r.jsx)(n.strong,{children:"all"})," errors. The document write could have already reached the remote instance and be processed, while only the answering fails.\nThe remote instance must be designed to handle this properly and to not crash on duplicate data transmissions.\nDepending on your use case, it might be ok to just write the duplicate document data again.\nBut for a more resilient error handling you could compare the last write timestamps or add a unique write id field to the document. This field can then be used to detect duplicates and ignore re-send data."]}),"\n",(0,r.jsxs)(n.p,{children:["Also the replication has an ",(0,r.jsx)(n.code,{children:".error$"})," stream that emits all ",(0,r.jsx)(n.code,{children:"RxError"})," objects that arise during replication.\nNotice that these errors contain an inner ",(0,r.jsx)(n.code,{children:".parameters.errors"})," field that contains the original error. Also they contain a ",(0,r.jsx)(n.code,{children:".parameters.direction"})," field that indicates if the error was thrown during ",(0,r.jsx)(n.code,{children:"pull"})," or ",(0,r.jsx)(n.code,{children:"push"}),". You can use these to properly handle errors. For example when the client is outdated, the server might respond with a ",(0,r.jsx)(n.code,{children:"426 Upgrade Required"})," error code that can then be used to force a page reload."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"error$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"((error) "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" error"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"parameters"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".errors "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"&&"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" error"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"parameters"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".errors["}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"] "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"&&"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" error"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"parameters"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".errors["}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"].code "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 426"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ) {"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // client is outdated -> enforce a page reload"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" location"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".reload"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,r.jsx)(n.h2,{id:"security",children:"Security"}),"\n",(0,r.jsxs)(n.p,{children:["Be aware that client side clocks can never be trusted. When you have a client-backend replication, the backend should overwrite the ",(0,r.jsx)(n.code,{children:"updatedAt"})," timestamp or use another field, when it receives the change from the client."]}),"\n",(0,r.jsx)(n.h2,{id:"rxreplicationstate",children:"RxReplicationState"}),"\n",(0,r.jsxs)(n.p,{children:["The function ",(0,r.jsx)(n.code,{children:"replicateRxCollection()"})," returns a ",(0,r.jsx)(n.code,{children:"RxReplicationState"})," that can be used to manage and observe the replication."]}),"\n",(0,r.jsx)(n.h3,{id:"observable",children:"Observable"}),"\n",(0,r.jsxs)(n.p,{children:["To observe the replication, the ",(0,r.jsx)(n.code,{children:"RxReplicationState"})," has some ",(0,r.jsx)(n.code,{children:"Observable"})," properties:"]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// emits each document that was received from the remote"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"received$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(doc));"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// emits each document that was send to the remote"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"sent$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(doc));"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// emits all errors that happen when running the push- & pull-handlers."})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"error$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(error "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(error));"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// emits true when the replication was canceled, false when not."})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"canceled$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(bool "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(bool));"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// emits true when a replication cycle is running, false when not."})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"active$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(bool "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(bool));"})]})]})})}),"\n",(0,r.jsx)(n.h3,{id:"awaitinitialreplication",children:"awaitInitialReplication()"}),"\n",(0,r.jsxs)(n.p,{children:["With ",(0,r.jsx)(n.code,{children:"awaitInitialReplication()"})," you can await the initial replication that is done when a full replication cycle was successful finished for the first time. The returned promise will never resolve if you cancel the replication before the initial replication can be done."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".awaitInitialReplication"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),"\n",(0,r.jsx)(n.h3,{id:"awaitinsync",children:"awaitInSync()"}),"\n",(0,r.jsxs)(n.p,{children:["Returns a ",(0,r.jsx)(n.code,{children:"Promise"})," that resolves when:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"awaitInitialReplication()"})," has emitted."]}),"\n",(0,r.jsx)(n.li,{children:"All local data is replicated with the remote."}),"\n",(0,r.jsx)(n.li,{children:"No replication cycle is running or in retry-state."}),"\n"]}),"\n",(0,r.jsxs)(n.admonition,{type:"warning",children:[(0,r.jsxs)(n.p,{children:["When ",(0,r.jsx)(n.code,{children:"multiInstance: true"})," and ",(0,r.jsx)(n.code,{children:"waitForLeadership: true"})," and another tab is already running the replication, ",(0,r.jsx)(n.code,{children:"awaitInSync()"})," will not resolve until the other tab is closed and the replication starts in this tab."]}),(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".awaitInSync"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})})]}),"\n",(0,r.jsxs)(n.admonition,{type:"warning",children:[(0,r.jsx)(n.mdxAdmonitionTitle,{}),(0,r.jsxs)(n.h4,{id:"awaitinitialreplication-and-awaitinsync-should-not-be-used-to-block-the-application",children:[(0,r.jsx)(n.code,{children:"awaitInitialReplication()"})," and ",(0,r.jsx)(n.code,{children:"awaitInSync()"})," should not be used to block the application"]}),(0,r.jsxs)(n.p,{children:["A common mistake in RxDB usage is when developers want to block the app usage until the application is in sync.\nOften they just ",(0,r.jsx)(n.code,{children:"await"})," the promise of ",(0,r.jsx)(n.code,{children:"awaitInitialReplication()"})," or ",(0,r.jsx)(n.code,{children:"awaitInSync()"})," and show a loading spinner until they resolve. This is dangerous and should not be done because:"]}),(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["When ",(0,r.jsx)(n.code,{children:"multiInstance: true"})," and ",(0,r.jsx)(n.code,{children:"waitForLeadership: true (default)"})," and another tab is already running the replication, ",(0,r.jsx)(n.code,{children:"awaitInitialReplication()"})," will not resolve until the other tab is closed and the replication starts in this tab."]}),"\n",(0,r.jsxs)(n.li,{children:["Your app can no longer be started when the device is offline because there the ",(0,r.jsx)(n.code,{children:"awaitInitialReplication()"})," will never resolve and the app cannot be used."]}),"\n"]}),(0,r.jsxs)(n.p,{children:["Instead you should store the last in-sync time in a ",(0,r.jsx)(n.a,{href:"/rx-local-document.html",children:"local document"})," and observe its value on all instances."]}),(0,r.jsx)(n.p,{children:"For example if you want to block clients from using the app if they have not been in sync for the last 24 hours, you could use this code:"}),(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// update last-in-sync-flag each time replication is in sync"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insertLocal"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'last-in-sync'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { time"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".catch"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// ensure flag exists"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"active$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".pipe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" mergeMap"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".awaitInSync"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".upsertLocal"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'last-in-sync'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { time"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Date"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".now"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() })"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// observe the flag and toggle loading spinner"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" showLoadingSpinner"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" oneDay"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 24"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" firstValueFrom"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".getLocal$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'last-in-sync'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".pipe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" filter"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(d "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" d"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'time'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Date"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".now"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"-"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" oneDay))"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" hideLoadingSpinner"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})})]}),"\n",(0,r.jsx)(n.h3,{id:"resync",children:"reSync()"}),"\n",(0,r.jsxs)(n.p,{children:["Triggers a ",(0,r.jsx)(n.code,{children:"RESYNC"})," cycle where the replication goes into ",(0,r.jsx)(n.a,{href:"#checkpoint-iteration",children:"checkpoint iteration"})," until the client is in sync with the backend. Used in unit tests or when no proper ",(0,r.jsx)(n.code,{children:"pull.stream$"})," can be implemented so that the client only knows that something has been changed but not what."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".reSync"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),"\n",(0,r.jsxs)(n.p,{children:["If your backend is not capable of sending events to the client at all, you could run ",(0,r.jsx)(n.code,{children:"reSync()"})," in an interval so that the client will automatically fetch server changes after some time at least."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// trigger RESYNC each 10 seconds."})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"setInterval"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".reSync"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,r.jsx)(n.h3,{id:"cancel",children:"cancel()"}),"\n",(0,r.jsx)(n.p,{children:"Cancels the replication. Returns a promise that resolved when everything has been cleaned up."}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cancel"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),"\n",(0,r.jsx)(n.h3,{id:"pause",children:"pause()"}),"\n",(0,r.jsxs)(n.p,{children:["Pauses a running replication. The replication can later be resumed with ",(0,r.jsx)(n.code,{children:"RxReplicationState.start()"}),"."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".pause"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".start"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// restart"})]})]})})}),"\n",(0,r.jsx)(n.h3,{id:"remove",children:"remove()"}),"\n",(0,r.jsxs)(n.p,{children:['Cancels the replication and deletes the metadata of the replication state. This can be used to restart the replication "from scratch". Calling ',(0,r.jsx)(n.code,{children:".remove()"})," will only delete the replication metadata, it will NOT delete the documents from the collection of the replication."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxReplicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),"\n",(0,r.jsx)(n.h3,{id:"isstopped",children:"isStopped()"}),"\n",(0,r.jsxs)(n.p,{children:["Returns ",(0,r.jsx)(n.code,{children:"true"})," if the replication is stopped. This can be if a non-live replication is finished or a replication got canceled."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".isStopped"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// true/false"})]})})})}),"\n",(0,r.jsx)(n.h3,{id:"ispaused",children:"isPaused()"}),"\n",(0,r.jsxs)(n.p,{children:["Returns ",(0,r.jsx)(n.code,{children:"true"})," if the replication is paused."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".isPaused"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// true/false"})]})})})}),"\n",(0,r.jsx)(n.h3,{id:"setting-a-custom-initialcheckpoint",children:"Setting a custom initialCheckpoint"}),"\n",(0,r.jsxs)(n.p,{children:["By default, the push replication will start from the beginning of time and push all documents from there to the remote.\nBy setting a custom ",(0,r.jsx)(n.code,{children:"push.initialCheckpoint"}),", you can tell the replication to only push writes that are newer than the given checkpoint."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// store the latest checkpoint of a collection"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastLocalCheckpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"checkpoint$"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(checkpoint "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastLocalCheckpoint "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint);"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// start the replication but only push documents that are newer than the lastLocalCheckpoint"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-custom-replication-with-init-checkpoint'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" handler"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" initialCheckpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastLocalCheckpoint"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,r.jsxs)(n.p,{children:["The same can be done for the other direction by setting a ",(0,r.jsx)(n.code,{children:"pull.initialCheckpoint"}),". Notice that here we need the remote checkpoint from the backend instead of the one from the RxDB storage."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// get the last pull checkpoint from the server"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" lastRemoteCheckpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'http://example.com/pull-checkpoint'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"))"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// start the replication but only pull documents that are newer than the lastRemoteCheckpoint"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-custom-replication-with-init-checkpoint'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" handler"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" initialCheckpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastRemoteCheckpoint"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,r.jsx)(n.h3,{id:"toggleondocumentvisible",children:"toggleOnDocumentVisible"}),"\n",(0,r.jsxs)(n.p,{children:["Ensures replication continues running when the document is ",(0,r.jsx)(n.code,{children:"visible"}),". This helps avoid situations where the leader-elected tab becomes stale or is hibernated by the browser to save battery.",(0,r.jsx)(n.br,{}),"\n","When the tab becomes hidden, replication is automatically paused; when the tab becomes visible again (or the instance becomes leader), replication resumes."]}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Default:"})," ",(0,r.jsx)(n.code,{children:"true"})]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" toggleOnDocumentVisible"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,r.jsx)(n.h2,{id:"attachment-replication",children:"Attachment replication"}),"\n",(0,r.jsxs)(n.p,{children:["Attachment replication is supported in the RxDB Sync Engine itself. However not all replication plugins support it.\nIf you start the replication with a collection which has ",(0,r.jsx)(n.a,{href:"/rx-attachment.html",children:"enabled RxAttachments"})," attachments data will be added to all push- and write data."]}),"\n",(0,r.jsxs)(n.p,{children:["The pushed documents will contain an ",(0,r.jsx)(n.code,{children:"_attachments"})," object which contains:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"The attachment meta data (id, length, digest) of all non-attachments"}),"\n",(0,r.jsx)(n.li,{children:"The full attachment data of all attachments that have been updated/added from the client."}),"\n",(0,r.jsx)(n.li,{children:"Deleted attachments are spared out in the pushed document."}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"With this data, the backend can decide onto which attachments must be deleted, added or overwritten."}),"\n",(0,r.jsx)(n.p,{children:"Accordingly, the pulled document must contain the same data, if the backend has a new document state with updated attachments."}),"\n",(0,r.jsx)(n.h2,{id:"pull-only-replication",children:"Pull-Only Replication"}),"\n",(0,r.jsx)(n.p,{children:"With the replication protocol it is possible to do pull only replications where data is pulled from a backend but not pushed from the client. RxDB implements some performance optimizations for these like not storing server metadata on pull only streams."}),"\n",(0,r.jsx)(n.h2,{id:"partial-sync-with-rxdb",children:"Partial Sync with RxDB"}),"\n",(0,r.jsx)(n.p,{children:"Suppose you're building a Minecraft-like voxel game where the world can expand in every direction. Storing the entire map locally for offline use is impossible because the dataset could be massive. Yet you still want a local-first design so players can edit the game world offline and sync back to the server later."}),"\n",(0,r.jsx)(n.h3,{id:"idea-one-collection-multiple-replications",children:"Idea: One Collection, Multiple Replications"}),"\n",(0,r.jsxs)(n.p,{children:["You might define a single RxDB collection called ",(0,r.jsx)(n.code,{children:"db.voxels"}),', where each document represents a block or "voxel" (with fields like id, chunkId, coordinates, and type). With RxDB you can, instead of setting up ',(0,r.jsx)(n.em,{children:"one"})," replication that tries to fetch ",(0,r.jsx)(n.em,{children:"all"})," voxels, you create ",(0,r.jsx)(n.strong,{children:"separate replication states"})," for each ",(0,r.jsx)(n.em,{children:"chunk"})," of the world the player is currently near."]}),"\n",(0,r.jsxs)(n.p,{children:["When the player enters a particular chunk (say ",(0,r.jsx)(n.code,{children:"chunk-123"}),"), you ",(0,r.jsx)(n.strong,{children:"start a replication"})," dedicated to that chunk. On the server side, you have endpoints to ",(0,r.jsx)(n.strong,{children:"pull"})," only that chunk's voxels (e.g., GET ",(0,r.jsx)(n.code,{children:"/api/voxels/pull?chunkId=123"}),") and ",(0,r.jsx)(n.strong,{children:"push"})," local changes back (e.g., POST ",(0,r.jsx)(n.code,{children:"/api/voxels/push?chunkId=123"}),"). RxDB handles them similarly to any other offline-first setup, but each replication is filtered to only that chunk's data."]}),"\n",(0,r.jsxs)(n.p,{children:["When the player leaves ",(0,r.jsx)(n.code,{children:"chunk-123"})," and no longer needs it, you ",(0,r.jsx)(n.strong,{children:"stop"})," that replication. If the player moves to ",(0,r.jsx)(n.code,{children:"chunk-124"}),", you start a new replication for chunk 124. This ensures the game only downloads and syncs data relevant to the player's immediate location. Meanwhile, all edits made offline remain safely stored in the local database until a network connection is available."]}),"\n",(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" activeReplications"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}; "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// chunkId -> replicationState"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" startChunkReplication"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(chunkId) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (activeReplications[chunkId]) "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationId"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'voxels-chunk-'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" chunkId;"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".voxels"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationId"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(checkpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" limit) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `/api/voxels/pull?chunkId="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"chunkId"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"&cp="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"checkpoint"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"&limit="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"limit"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(changedDocs) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`/api/voxels/push?chunkId="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"chunkId"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" activeReplications[chunkId] "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationState;"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" stopChunkReplication"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(chunkId) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" rep"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" activeReplications[chunkId];"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (rep) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" rep"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cancel"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" delete"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" activeReplications[chunkId];"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Called whenever the player's location changes; "})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// dynamically start/stop replication for nearby chunks."})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"function"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" onPlayerMove"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(neighboringChunkIds) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" neighboringChunkIds"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".forEach"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(startChunkReplication);"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Object"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".keys"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(activeReplications)"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".forEach"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(cid "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"neighboringChunkIds"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".includes"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(cid)) {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" stopChunkReplication"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(cid);"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,r.jsx)(n.h3,{id:"diffy-sync-when-revisiting-a-chunk",children:"Diffy-Sync when Revisiting a Chunk"}),"\n",(0,r.jsxs)(n.p,{children:['An added benefit of this multi-replication-state design is checkpointing. Each replication state has a unique "replication identifier," so the next time the player returns to ',(0,r.jsx)(n.code,{children:"chunk-123"}),", the local database knows what it already has and only fetches the differences without the need to re-download the entire chunk."]}),"\n",(0,r.jsx)(n.h3,{id:"partial-sync-in-a-local-first-business-application",children:"Partial Sync in a Local-First Business Application"}),"\n",(0,r.jsx)(n.p,{children:'Though a voxel world is an intuitive example, the same technique applies in enterprise scenarios where data sets are large but each user only needs a specific subset. You could spin up a new replication for each "permission group" or "region," so users only sync the records they\'re allowed to see. Or in a CRM, the replication might be filtered by the specific accounts or projects a user is currently handling. As soon as they switch to a different project, you stop the old replication and start one for the new scope.'}),"\n",(0,r.jsxs)(n.p,{children:["This ",(0,r.jsx)(n.strong,{children:"chunk-based"})," or ",(0,r.jsx)(n.strong,{children:"scope-based"}),' replication pattern keeps your local storage lean, reduces network overhead, and still gives users the offline, instant-feedback experience that local-first apps are known for. By dynamically creating (and canceling) replication states, you retain tight control over bandwidth usage and make the infinite (or very large) feasible. In a production app you would also "flag" the entities (with a ',(0,r.jsx)(n.code,{children:"pull.modifier"}),") by which replication state they came from, so that you can clean up the parts that you no longer need. --\x3e"]}),"\n",(0,r.jsx)(n.h2,{id:"faq",children:"FAQ"}),"\n",(0,r.jsxs)(s,{children:[(0,r.jsx)("summary",{children:"I have infinite loops in my replication, how to debug?"}),(0,r.jsx)("div",{children:(0,r.jsxs)(n.p,{children:["When you have infinite loops in your replication or random re-runs of http requests after some time, the reason is likely that your pull-handler\nis crashing. The debug this, add a log to the error$ handler to debug it. ",(0,r.jsx)(n.code,{children:"myRxReplicationState.error$.subscribe(err => console.log('error$', err))"}),"."]})})]})]})}function d(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},8453(e,n,s){s.d(n,{R:()=>t,x:()=>a});var i=s(6540);const r={},o=i.createContext(r);function t(e){const n=i.useContext(o);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:t(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/6bfb0089.d52978b6.js b/docs/assets/js/6bfb0089.d52978b6.js
deleted file mode 100644
index 2d2e9bf8ed9..00000000000
--- a/docs/assets/js/6bfb0089.d52978b6.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6422],{6235(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>i,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"adapters","title":"PouchDB Adapters","description":"When you use PouchDB RxStorage, there are many adapters that define where the data has to be stored.","source":"@site/docs/adapters.md","sourceDirName":".","slug":"/adapters.html","permalink":"/adapters.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"PouchDB Adapters","slug":"adapters.html"}}');var a=n(4848),o=n(8453);const i={title:"PouchDB Adapters",slug:"adapters.html"},l="PouchDB Adapters",t={},d=[{value:"Memory",id:"memory",level:2},{value:"Memdown",id:"memdown",level:2},{value:"IndexedDB",id:"indexeddb",level:2},{value:"IndexedDB",id:"indexeddb-1",level:2},{value:"Websql",id:"websql",level:2},{value:"leveldown",id:"leveldown",level:2},{value:"Node-Websql",id:"node-websql",level:2},{value:"react-native-sqlite",id:"react-native-sqlite",level:2},{value:"asyncstorage",id:"asyncstorage",level:2},{value:"asyncstorage-down",id:"asyncstorage-down",level:2},{value:"cordova-sqlite",id:"cordova-sqlite",level:2}];function c(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.header,{children:(0,a.jsx)(s.h1,{id:"pouchdb-adapters",children:"PouchDB Adapters"})}),"\n",(0,a.jsxs)(s.p,{children:["When you use PouchDB ",(0,a.jsx)(s.code,{children:"RxStorage"}),", there are many adapters that define where the data has to be stored.\nDepending on which environment you work in, you can choose between different adapters. For example, in the browser you want to store the data inside of IndexedDB but on NodeJS you want to store the data on the filesystem."]}),"\n",(0,a.jsx)(s.p,{children:"This page is an overview over the different adapters with recommendations on what to use where."}),"\n",(0,a.jsx)(s.hr,{}),"\n",(0,a.jsx)(s.admonition,{type:"warning",children:(0,a.jsxs)(s.p,{children:["The PouchDB RxStorage ",(0,a.jsx)(s.a,{href:"/rx-storage-pouchdb.html",children:"is removed from RxDB"})," and can no longer be used in new projects. You should switch to a different ",(0,a.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"}),"."]})}),"\n",(0,a.jsx)(s.hr,{}),"\n",(0,a.jsxs)(s.p,{children:["Please always ensure that your pouchdb adapter-version is the same as ",(0,a.jsx)(s.code,{children:"pouchdb-core"})," in the ",(0,a.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/master/package.json",children:"rxdb package.json"}),". Otherwise, you might have strange problems."]}),"\n",(0,a.jsx)(s.h1,{id:"any-environment",children:"Any environment"}),"\n",(0,a.jsx)(s.h2,{id:"memory",children:"Memory"}),"\n",(0,a.jsx)(s.p,{children:"In any environment, you can use the memory-adapter. It stores the data in the javascript runtime memory. This means it is not persistent and the data is lost when the process terminates."}),"\n",(0,a.jsx)(s.p,{children:"Use this adapter when:"}),"\n",(0,a.jsxs)(s.ul,{children:["\n",(0,a.jsx)(s.li,{children:"You want to have really good performance"}),"\n",(0,a.jsx)(s.li,{children:"You do not want persistent state, for example in your test suite"}),"\n"]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStoragePouch"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/pouchdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-memory --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-memory'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'memory'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"memdown",children:"Memdown"}),"\n",(0,a.jsxs)(s.p,{children:["With RxDB you can also use adapters that implement ",(0,a.jsx)(s.a,{href:"https://github.com/Level/abstract-leveldown",children:"abstract-leveldown"})," like the memdown-adapter."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install memdown --save"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-leveldb --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-leveldb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")); "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// leveldown adapters need the leveldb plugin to work"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" memdown"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'memdown'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(memdown) "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the full leveldown-module"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h1,{id:"browser",children:"Browser"}),"\n",(0,a.jsx)(s.h2,{id:"indexeddb",children:"IndexedDB"}),"\n",(0,a.jsxs)(s.p,{children:["The IndexedDB adapter stores the data inside of ",(0,a.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API",children:"IndexedDB"})," use this in browsers environments as default."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-idb --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-idb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"indexeddb-1",children:"IndexedDB"}),"\n",(0,a.jsxs)(s.p,{children:["A reimplementation of the indexeddb adapter which uses native secondary indexes. Should have a much better performance but can behave ",(0,a.jsx)(s.a,{href:"https://github.com/pouchdb/pouchdb/tree/master/packages/node_modules/pouchdb-adapter-indexeddb#differences-between-couchdb-and-pouchdbs-find-implementations-under-indexeddb",children:"different on some edge cases"}),"."]}),"\n",(0,a.jsx)(s.admonition,{type:"note",children:(0,a.jsxs)(s.p,{children:["Multiple users have reported problems with this adapter. It is ",(0,a.jsx)(s.strong,{children:"not"})," recommended to use this adapter."]})}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-indexeddb --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-indexeddb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'indexeddb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"websql",children:"Websql"}),"\n",(0,a.jsxs)(s.p,{children:["This adapter stores the data inside of websql. It has a different performance behavior. ",(0,a.jsx)(s.a,{href:"https://softwareengineering.stackexchange.com/questions/220254/why-is-web-sql-database-deprecated",children:"Websql is deprecated"}),". You should not use the websql adapter unless you have a really good reason."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-websql --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-websql'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'websql'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h1,{id:"nodejs",children:"NodeJS"}),"\n",(0,a.jsx)(s.h2,{id:"leveldown",children:"leveldown"}),"\n",(0,a.jsxs)(s.p,{children:["This adapter uses a ",(0,a.jsx)(s.a,{href:"https://github.com/Level/leveldown",children:"LevelDB C++ binding"})," to store that data on the filesystem. It has the best performance compared to other filesystem adapters. This adapter can ",(0,a.jsx)(s.strong,{children:"not"})," be used when multiple nodejs-processes access the same filesystem folders for storage."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install leveldown --save"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-leveldb --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-leveldb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")); "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// leveldown adapters need the leveldb plugin to work"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" leveldown"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'leveldown'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(leveldown) "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the full leveldown-module"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// or use a specific folder to store the data"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/root/user/project/mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(leveldown) "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the full leveldown-module"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"node-websql",children:"Node-Websql"}),"\n",(0,a.jsxs)(s.p,{children:["This adapter uses the ",(0,a.jsx)(s.a,{href:"https://github.com/nolanlawson/node-websql",children:"node-websql"}),"-shim to store data on the filesystem. Its advantages are that it does not need a leveldb build and it can be used when multiple nodejs-processes use the same database-files."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-node-websql --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-node-websql'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'websql'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the name of your adapter"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// or use a specific folder to store the data"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/root/user/project/mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'websql'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the name of your adapter"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h1,{id:"react-native",children:"React-Native"}),"\n",(0,a.jsx)(s.h2,{id:"react-native-sqlite",children:"react-native-sqlite"}),"\n",(0,a.jsxs)(s.p,{children:["Uses ReactNative SQLite as storage. Claims to be much faster than the asyncstorage adapter.\nTo use it, you have to do some steps from ",(0,a.jsx)(s.a,{href:"https://dev.to/craftzdog/hacking-pouchdb-to-use-on-react-native-1gjh",children:"this tutorial"}),"."]}),"\n",(0,a.jsxs)(s.p,{children:["First install ",(0,a.jsx)(s.code,{children:"pouchdb-adapter-react-native-sqlite"})," and ",(0,a.jsx)(s.code,{children:"react-native-sqlite-2"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" pouchdb-adapter-react-native-sqlite"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" react-native-sqlite-2"})]})})})}),"\n",(0,a.jsxs)(s.p,{children:["Then you have to ",(0,a.jsx)(s.a,{href:"https://facebook.github.io/react-native/docs/linking-libraries-ios",children:"link"})," the library."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"react-native"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" link"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" react-native-sqlite-2"})]})})})}),"\n",(0,a.jsx)(s.p,{children:"You also have to add some polyfills which are need but not included in react-native."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" base-64"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" events"})]})})})}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { decode"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encode } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'base-64'"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"if"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"global"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".btoa) {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" global"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".btoa "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encode;"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"if"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"global"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".atob) {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" global"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".atob "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" decode;"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Avoid using node dependent modules"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"process"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".browser "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),"\n",(0,a.jsx)(s.p,{children:"Then you can use it inside of your code."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStoragePouch } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/pouchdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLite "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'react-native-sqlite-2'"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLiteAdapterFactory "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'pouchdb-adapter-react-native-sqlite'"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" SQLiteAdapter"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" SQLiteAdapterFactory"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(SQLite)"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(SQLiteAdapter);"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-http'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'react-native-sqlite'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the name of your adapter"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"asyncstorage",children:"asyncstorage"}),"\n",(0,a.jsxs)(s.p,{children:["Uses react-native's ",(0,a.jsx)(s.a,{href:"https://facebook.github.io/react-native/docs/asyncstorage",children:"asyncstorage"}),"."]}),"\n",(0,a.jsx)(s.admonition,{type:"note",children:(0,a.jsxs)(s.p,{children:["There are ",(0,a.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/issues/2286",children:"known problems"})," with this adapter and it is ",(0,a.jsx)(s.strong,{children:"not"})," recommended to use it."]})}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-asyncstorage --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-asyncstorage'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'node-asyncstorage'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the name of your adapter"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"asyncstorage-down",children:"asyncstorage-down"}),"\n",(0,a.jsx)(s.p,{children:"A leveldown adapter that stores on asyncstorage."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-asyncstorage-down --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-leveldb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")); "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// leveldown adapters need the leveldb plugin to work"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" asyncstorageDown"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'asyncstorage-down'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(asyncstorageDown) "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the full leveldown-module"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h1,{id:"cordova--phonegap--capacitor",children:"Cordova / Phonegap / Capacitor"}),"\n",(0,a.jsx)(s.h2,{id:"cordova-sqlite",children:"cordova-sqlite"}),"\n",(0,a.jsxs)(s.p,{children:["Uses cordova's global ",(0,a.jsx)(s.code,{children:"cordova.sqlitePlugin"}),". It can be used with cordova and capacitor."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// npm install pouchdb-adapter-cordova-sqlite --save"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addPouchPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"require"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'pouchdb-adapter-cordova-sqlite'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"));"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * In capacitor/cordova you have to wait until all plugins are loaded and 'window.sqlitePlugin'"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * can be accessed."})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * This function waits until document deviceready is called which ensures that everything is loaded."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" https://cordova.apache.org/docs/de/latest/cordova/events/events.deviceready.html"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" awaitCapacitorDeviceReady"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Promise"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"void"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"> {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(res "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" document"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addEventListener"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'deviceready'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" res"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(){"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // first wait until the deviceready event is fired"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" awaitCapacitorDeviceReady"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStoragePouch"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'cordova-sqlite'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // pouch settings are passed as second parameter"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // for ios devices, the cordova-sqlite adapter needs to know where to save the data."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" iosDatabaseLocation"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Library'"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,a.jsx)(s,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},8453(e,s,n){n.d(s,{R:()=>i,x:()=>l});var r=n(6540);const a={},o=r.createContext(a);function i(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/6cbff7c2.db294439.js b/docs/assets/js/6cbff7c2.db294439.js
deleted file mode 100644
index dbf309dea20..00000000000
--- a/docs/assets/js/6cbff7c2.db294439.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7408],{7010(e,s,r){r.r(s),r.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>a,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"rx-storage-opfs","title":"Supercharged OPFS Database with RxDB","description":"Discover how to harness the Origin Private File System with RxDB\'s OPFS RxStorage for unrivaled performance and security in client-side data storage.","source":"@site/docs/rx-storage-opfs.md","sourceDirName":".","slug":"/rx-storage-opfs.html","permalink":"/rx-storage-opfs.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Supercharged OPFS Database with RxDB","slug":"rx-storage-opfs.html","description":"Discover how to harness the Origin Private File System with RxDB\'s OPFS RxStorage for unrivaled performance and security in client-side data storage."},"sidebar":"tutorialSidebar","previous":{"title":"IndexedDB \ud83d\udc51 (Browser, Capacitor)","permalink":"/rx-storage-indexeddb.html"},"next":{"title":"Memory","permalink":"/rx-storage-memory.html"}}');var i=r(4848),o=r(8453);const a={title:"Supercharged OPFS Database with RxDB",slug:"rx-storage-opfs.html",description:"Discover how to harness the Origin Private File System with RxDB's OPFS RxStorage for unrivaled performance and security in client-side data storage."},t="Origin Private File System (OPFS) Database with the RxDB OPFS-RxStorage",l={},c=[{value:"What is OPFS",id:"what-is-opfs",level:2},{value:"OPFS limitations",id:"opfs-limitations",level:3},{value:"How the OPFS API works",id:"how-the-opfs-api-works",level:2},{value:"OPFS performance",id:"opfs-performance",level:2},{value:"Using OPFS as RxStorage in RxDB",id:"using-opfs-as-rxstorage-in-rxdb",level:2},{value:"Using OPFS in the main thread instead of a worker",id:"using-opfs-in-the-main-thread-instead-of-a-worker",level:2},{value:"Building a custom worker.js",id:"building-a-custom-workerjs",level:2},{value:"Setting usesRxDatabaseInWorker when a RxDatabase is also used inside of the worker",id:"setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker",level:2},{value:"OPFS in Electron, React-Native or Capacitor.js",id:"opfs-in-electron-react-native-or-capacitorjs",level:2},{value:"Difference between File System Access API and Origin Private File System (OPFS)",id:"difference-between-file-system-access-api-and-origin-private-file-system-opfs",level:2},{value:"Learn more about OPFS:",id:"learn-more-about-opfs",level:2}];function d(e){const s={a:"a",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"origin-private-file-system-opfs-database-with-the-rxdb-opfs-rxstorage",children:"Origin Private File System (OPFS) Database with the RxDB OPFS-RxStorage"})}),"\n",(0,i.jsxs)(s.p,{children:["With the ",(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," OPFS storage you can build a fully featured database on top of the ",(0,i.jsx)(s.a,{href:"https://web.dev/opfs",children:"Origin Private File System"})," (OPFS) browser API. Compared to other storage solutions, it has a way better performance."]}),"\n",(0,i.jsx)(s.h2,{id:"what-is-opfs",children:"What is OPFS"}),"\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.strong,{children:"Origin Private File System (OPFS)"})," is a native browser storage API that allows web applications to manage files in a private, sandboxed, ",(0,i.jsx)(s.strong,{children:"origin-specific virtual filesystem"}),". Unlike ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," and ",(0,i.jsx)(s.a,{href:"/articles/localstorage.html",children:"LocalStorage"}),", which are optimized as object/key-value storage, OPFS provides more granular control for file operations, enabling byte-by-byte access, file streaming, and even low-level manipulations.\nOPFS is ideal for applications requiring ",(0,i.jsx)(s.strong,{children:"high-performance"})," file operations (",(0,i.jsx)(s.strong,{children:"3x-4x faster compared to IndexedDB"}),") inside of a client-side application, offering advantages like improved speed, more efficient use of resources, and enhanced security and privacy features."]}),"\n",(0,i.jsx)(s.h3,{id:"opfs-limitations",children:"OPFS limitations"}),"\n",(0,i.jsxs)(s.p,{children:["From the beginning of 2023, the Origin Private File System API is supported by ",(0,i.jsx)(s.a,{href:"https://caniuse.com/native-filesystem-api",children:"all modern browsers"})," like Safari, Chrome, Edge and Firefox. Only Internet Explorer is not supported and likely will never get support."]}),"\n",(0,i.jsxs)(s.p,{children:["It is important to know that the most performant synchronous methods like ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/FileSystemSyncAccessHandle/read",children:(0,i.jsx)(s.code,{children:"read()"})})," and ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/FileSystemSyncAccessHandle/write",children:(0,i.jsx)(s.code,{children:"write()"})})," of the OPFS API are ",(0,i.jsxs)(s.strong,{children:["only available inside of a ",(0,i.jsx)(s.a,{href:"/rx-storage-worker.html",children:"WebWorker"})]}),".\nThey cannot be used in the main thread, an iFrame or even a ",(0,i.jsx)(s.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker"}),".\nThe OPFS ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle",children:(0,i.jsx)(s.code,{children:"createSyncAccessHandle()"})})," method that gives you access to the synchronous methods is not exposed in the main thread, only in a Worker."]}),"\n",(0,i.jsxs)(s.p,{children:["While there is no concrete ",(0,i.jsx)(s.strong,{children:"data size limit"})," defined by the API, browsers will refuse to store more ",(0,i.jsx)(s.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"data at some point"}),".\nIf no more data can be written, a ",(0,i.jsx)(s.code,{children:"QuotaExceededError"})," is thrown which should be handled by the application, like showing an error message to the user."]}),"\n",(0,i.jsx)(s.h2,{id:"how-the-opfs-api-works",children:"How the OPFS API works"}),"\n",(0,i.jsxs)(s.p,{children:["The OPFS API is pretty straightforward to use. First you get the root filesystem. Then you can create files and directories on that. Notice that whenever you ",(0,i.jsx)(s.em,{children:"synchronously"})," write to, or read from a file, an ",(0,i.jsx)(s.code,{children:"ArrayBuffer"})," must be used that contains the data. It is not possible to synchronously write plain strings or objects into the file. Therefore the ",(0,i.jsx)(s.code,{children:"TextEncoder"})," and ",(0,i.jsx)(s.code,{children:"TextDecoder"})," API must be used."]}),"\n",(0,i.jsxs)(s.p,{children:["Also notice that some of the methods of ",(0,i.jsx)(s.code,{children:"FileSystemSyncAccessHandle"})," ",(0,i.jsx)(s.a,{href:"https://developer.chrome.com/blog/sync-methods-for-accesshandles",children:"have been asynchronous"})," in the past, but are synchronous since Chromium 108. To make it less confusing, we just use ",(0,i.jsx)(s.code,{children:"await"})," in front of them, so it will work in both cases."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Access the root directory of the origin's private file system."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" root"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" navigator"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getDirectory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create a subdirectory."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" diaryDirectory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" root"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getDirectoryHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'subfolder'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" create"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create a new file named 'example.txt'."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" fileHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" diaryDirectory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getFileHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'example.txt'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" create"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create a FileSystemSyncAccessHandle on the file."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" fileHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".createSyncAccessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Write a sentence to the file."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" writeBuffer "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" TextEncoder"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".encode"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Hello from RxDB'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" writeSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".write"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(writeBuffer);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Read file and transform data to string."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" readBuffer"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Uint8Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(writeSize);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" readSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".read"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(readBuffer"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { at"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" contentAsString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" TextDecoder"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".decode"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(readBuffer);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Write an exclamation mark to the end of the file."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"writeBuffer "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" TextEncoder"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".encode"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'!'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".write"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(writeBuffer"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { at"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" readSize });"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Truncate file to 10 bytes."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".truncate"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Get the new size of the file."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" fileSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Persist changes to disk."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".flush"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Always close FileSystemSyncAccessHandle if done, so others can open the file again."})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" accessHandle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".close"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsxs)(s.p,{children:["A more detailed description of the OPFS API can be found ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system",children:"on MDN"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"opfs-performance",children:"OPFS performance"}),"\n",(0,i.jsxs)(s.p,{children:["Because the Origin Private File System API provides low-level access to binary files, it is much faster compared to ",(0,i.jsx)(s.a,{href:"/slow-indexeddb.html",children:"IndexedDB"})," or ",(0,i.jsx)(s.a,{href:"/articles/localstorage.html",children:"localStorage"}),". According to the ",(0,i.jsx)(s.a,{href:"https://pubkey.github.io/client-side-databases/database-comparison/index.html",children:"storage performance test"}),", OPFS is up to 2x times faster on plain inserts when a new file is created on each write. Reads are even faster."]}),"\n",(0,i.jsxs)(s.p,{children:["A good comparison about real world scenarios, are the ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance results"})," of the various RxDB storages. Here it shows that reads are up to 4x faster compared to IndexedDB, even with complex queries:"]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"700"})}),"\n",(0,i.jsx)(s.h2,{id:"using-opfs-as-rxstorage-in-rxdb",children:"Using OPFS as RxStorage in RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["The OPFS ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," itself must run inside a WebWorker. Therefore we use the ",(0,i.jsx)(s.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage"})," and let it point to the prebuild ",(0,i.jsx)(s.code,{children:"opfs.worker.js"})," file that comes shipped with RxDB Premium \ud83d\udc51."]}),"\n",(0,i.jsxs)(s.p,{children:["Notice that the OPFS RxStorage is part of the ",(0,i.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"})," plugin that must be purchased."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageWorker } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageWorker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * This file must be statically served from a webserver."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * You might want to first copy it somewhere outside of"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * your node_modules folder."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'node_modules/rxdb-premium/dist/workers/opfs.worker.js'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"using-opfs-in-the-main-thread-instead-of-a-worker",children:"Using OPFS in the main thread instead of a worker"}),"\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.code,{children:"createSyncAccessHandle"})," method from the Filesystem API is only available inside of a Webworker. Therefore you cannot use ",(0,i.jsx)(s.code,{children:"getRxStorageOPFS()"})," in the main thread. But there is a slightly slower way to access the virtual filesystem from the main thread. RxDB support the ",(0,i.jsx)(s.code,{children:"getRxStorageOPFSMainThread()"})," for that. Notice that this uses the ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createWritable",children:"createWritable"})," function which is not supported in safari."]}),"\n",(0,i.jsx)(s.p,{children:"Using OPFS from the main thread can have benefits because not having to cross the worker bridge can reduce latence in reads and writes."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageOPFSMainThread } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-opfs'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageOPFSMainThread"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.h2,{id:"building-a-custom-workerjs",children:["Building a custom ",(0,i.jsx)(s.code,{children:"worker.js"})]}),"\n",(0,i.jsxs)(s.p,{children:["When you want to run additional plugins like storage wrappers or replication ",(0,i.jsx)(s.strong,{children:"inside"})," of the worker, you have to build your own ",(0,i.jsx)(s.code,{children:"worker.js"})," file. You can do that similar to other workers by calling ",(0,i.jsx)(s.code,{children:"exposeWorkerRxStorage"})," like described in the ",(0,i.jsx)(s.a,{href:"/rx-storage-worker.html",children:"worker storage plugin"}),"."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// inside of the worker.js file"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageOPFS } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-opfs'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { exposeWorkerRxStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-worker'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageOPFS"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"exposeWorkerRxStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.h2,{id:"setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker",children:["Setting ",(0,i.jsx)(s.code,{children:"usesRxDatabaseInWorker"})," when a RxDatabase is also used inside of the worker"]}),"\n",(0,i.jsxs)(s.p,{children:["When you use the OPFS inside of a worker, it will internally use strings to represent operation results. This has the benefit that transferring strings from the worker to the main thread, is way faster compared to complex json objects. The ",(0,i.jsx)(s.code,{children:"getRxStorageWorker()"})," will automatically decode these strings on the main thread so that the data can be used by the RxDatabase."]}),"\n",(0,i.jsxs)(s.p,{children:["But using a RxDatabase ",(0,i.jsx)(s.strong,{children:"inside"})," of your worker can make sense for example when you want to move the ",(0,i.jsx)(s.a,{href:"/replication.html",children:"replication"})," with a server. To enable this, you have to set ",(0,i.jsx)(s.code,{children:"usesRxDatabaseInWorker"})," to ",(0,i.jsx)(s.code,{children:"true"}),":"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// inside of the worker.js file"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageOPFS } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-opfs'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageOPFS"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" usesRxDatabaseInWorker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["If you forget to set this and still create and use a ",(0,i.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"})," inside of the worker, you might get the error message",(0,i.jsx)(s.code,{children:"or"}),"Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')`."]}),"\n",(0,i.jsx)(s.h2,{id:"opfs-in-electron-react-native-or-capacitorjs",children:"OPFS in Electron, React-Native or Capacitor.js"}),"\n",(0,i.jsx)(s.p,{children:"Origin Private File System is a browser API that is only accessible in browsers. Other JavaScript like React-Native or Node.js, do not support it."}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Electron"})," has two JavaScript contexts: the browser (chromium) context and the Node.js context. While you could use the OPFS API in the browser context, it is not recommended. Instead you should use the Filesystem API of Node.js and then only transfer the relevant data with the ",(0,i.jsx)(s.a,{href:"https://www.electronjs.org/de/docs/latest/api/ipc-renderer",children:"ipcRenderer"}),". With RxDB that is pretty easy to configure:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["In the ",(0,i.jsx)(s.code,{children:"main.js"}),", expose the ",(0,i.jsx)(s.a,{href:"/rx-storage-filesystem-node.html",children:"Node Filesystem"})," storage with the ",(0,i.jsx)(s.code,{children:"exposeIpcMainRxStorage()"})," that comes with the ",(0,i.jsx)(s.a,{href:"/electron.html",children:"electron plugin"})]}),"\n",(0,i.jsxs)(s.li,{children:["In the browser context, access the main storage with the ",(0,i.jsx)(s.code,{children:"getRxStorageIpcRenderer()"})," method."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"React Native"})," (and Expo) does not have an OPFS API. You could use the ReactNative Filesystem to directly write data. But to get a fully featured database like RxDB it is easier to use the ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," which starts an SQLite database inside of the ReactNative app and uses that to do the database operations."]}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Capacitor.js"})," is able to access the OPFS API."]}),"\n",(0,i.jsxs)(s.h2,{id:"difference-between-file-system-access-api-and-origin-private-file-system-opfs",children:["Difference between ",(0,i.jsx)(s.code,{children:"File System Access API"})," and ",(0,i.jsx)(s.code,{children:"Origin Private File System (OPFS)"})]}),"\n",(0,i.jsxs)(s.p,{children:["Often developers are confused with the differences between the ",(0,i.jsx)(s.code,{children:"File System Access API"})," and the ",(0,i.jsx)(s.code,{children:"Origin Private File System (OPFS)"}),"."]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["The ",(0,i.jsx)(s.code,{children:"File System Access API"})," provides access to the files on the device file system, like the ones shown in the file explorer of the operating system. To use the File System API, the user has to actively select the files from a filepicker."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.code,{children:"Origin Private File System (OPFS)"})," is a sub-part of the ",(0,i.jsx)(s.code,{children:"File System Standard"})," and it only describes the things you can do with the filesystem root from ",(0,i.jsx)(s.code,{children:"navigator.storage.getDirectory()"}),". OPFS writes to a ",(0,i.jsx)(s.strong,{children:"sandboxed"})," filesystem, not visible to the user. Therefore the user does not have to actively select or allow the data access."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"learn-more-about-opfs",children:"Learn more about OPFS:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"https://webkit.org/blog/12257/the-file-system-access-api-with-origin-private-file-system/",children:"WebKit: The File System API with Origin Private File System"})}),"\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"https://caniuse.com/native-filesystem-api",children:"Browser Support"})}),"\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.a,{href:"https://pubkey.github.io/client-side-databases/database-comparison/index.html",children:"Performance Test Tool"})}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>t});var n=r(6540);const i={},o=n.createContext(i);function a(e){const s=n.useContext(o);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),n.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/6fa8aa1a.10d4f0d3.js b/docs/assets/js/6fa8aa1a.10d4f0d3.js
deleted file mode 100644
index ecb2f5dca32..00000000000
--- a/docs/assets/js/6fa8aa1a.10d4f0d3.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1264],{5379(e,t,a){a.r(t),a.d(t,{default:()=>i});var r=a(544),n=a(4848);function i(){return(0,r.default)({sem:{id:"gads",metaTitle:"The NoSQL alternative for PouchDB",title:(0,n.jsxs)(n.Fragment,{children:["The ",(0,n.jsx)("b",{children:"modern"})," alternative for"," ",(0,n.jsx)("b",{children:"PouchDB"})]}),text:(0,n.jsx)(n.Fragment,{children:"Store data inside the Browser to build high performance realtime applications that sync data from the backend and even work when offline."})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/6fd28feb.45da1980.js b/docs/assets/js/6fd28feb.45da1980.js
deleted file mode 100644
index 0f6772738e0..00000000000
--- a/docs/assets/js/6fd28feb.45da1980.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4618],{5989(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>r,default:()=>h,frontMatter:()=>t,metadata:()=>a,toc:()=>d});const a=JSON.parse('{"id":"rx-storage-foundationdb","title":"RxDB on FoundationDB - Performance at Scale","description":"Combine FoundationDB\'s reliability with RxDB\'s indexing and schema validation. Build scalable apps with faster queries and real-time data.","source":"@site/docs/rx-storage-foundationdb.md","sourceDirName":".","slug":"/rx-storage-foundationdb.html","permalink":"/rx-storage-foundationdb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB on FoundationDB - Performance at Scale","slug":"rx-storage-foundationdb.html","description":"Combine FoundationDB\'s reliability with RxDB\'s indexing and schema validation. Build scalable apps with faster queries and real-time data."},"sidebar":"tutorialSidebar","previous":{"title":"DenoKV","permalink":"/rx-storage-denokv.html"},"next":{"title":"Schema Validation","permalink":"/schema-validation.html"}}');var i=s(4848),o=s(8453);const t={title:"RxDB on FoundationDB - Performance at Scale",slug:"rx-storage-foundationdb.html",description:"Combine FoundationDB's reliability with RxDB's indexing and schema validation. Build scalable apps with faster queries and real-time data."},r="RxDB Database on top of FoundationDB",l={},d=[{value:"Features of RxDB+FoundationDB",id:"features-of-rxdbfoundationdb",level:2},{value:"Installation",id:"installation",level:2},{value:"Usage",id:"usage",level:2},{value:"Multi Instance",id:"multi-instance",level:2}];function c(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"rxdb-database-on-top-of-foundationdb",children:"RxDB Database on top of FoundationDB"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://www.foundationdb.org/",children:"FoundationDB"})," is a distributed key-value store designed to handle large volumes of structured data across clusters of computers while maintaining high levels of performance, scalability, and fault tolerance. While FoundationDB itself only can store and query key-value pairs, it lacks more advanced features like complex queries, encryption and replication."]}),"\n",(0,i.jsxs)(n.p,{children:["With the FoundationDB based ",(0,i.jsx)(n.a,{href:"/rx-storage.html",children:"RxStorage"})," of ",(0,i.jsx)(n.a,{href:"https://rxdb.info/",children:"RxDB"})," you can combine the benefits of FoundationDB while having a fully featured, high performance NoSQL database."]}),"\n",(0,i.jsx)(n.h2,{id:"features-of-rxdbfoundationdb",children:"Features of RxDB+FoundationDB"}),"\n",(0,i.jsx)(n.p,{children:"Using RxDB on top of FoundationDB, gives you many benefits compare to using the plain FoundationDB API:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Indexes"}),": In RxDB with a FoundationDB storage layer, indexes are used to optimize query performance, allowing for fast and efficient data retrieval even in large datasets. You can define single and compound indexes with the ",(0,i.jsx)(n.a,{href:"/rx-schema.html",children:"RxDB schema"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Schema Based Data Model"}),": Utilizing a ",(0,i.jsx)(n.a,{href:"/rx-schema.html",children:"jsonschema"})," based data model, the system offers a highly structured and versatile approach to organizing and ",(0,i.jsx)(n.a,{href:"/schema-validation.html",children:"validating data"}),", ensuring consistency and clarity in database interactions."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Complex Queries"}),": The system supports complex ",(0,i.jsx)(n.a,{href:"/rx-query.html",children:"NoSQL queries"}),", allowing for advanced data manipulation and retrieval, tailored to specific needs and intricate data relationships. For example you can do ",(0,i.jsx)(n.code,{children:"$regex"})," or ",(0,i.jsx)(n.code,{children:"$or"})," queries which is hardy possible with the plain key-value access of FoundationDB."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Observable Queries & Documents"}),": RxDB's observable queries and documents feature ensures real-time updates and synchronization, providing dynamic and responsive data interactions in applications."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Compression"}),": RxDB employs data ",(0,i.jsx)(n.a,{href:"/key-compression.html",children:"compression techniques"})," to reduce storage requirements and enhance transmission efficiency, making it more cost-effective and faster, especially for large volumes of data. You can compress the ",(0,i.jsx)(n.a,{href:"/key-compression.html",children:"NoSQL document"})," data, but also the ",(0,i.jsx)(n.a,{href:"/rx-attachment.html#attachment-compression",children:"binary attachments"})," data."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Attachments"}),": RxDB supports the storage and management of ",(0,i.jsx)(n.a,{href:"/rx-attachment.html",children:"attachments"})," which allowing for the seamless inclusion of binary data like images or documents alongside structured data within the database."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"installation",children:"Installation"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Install the ",(0,i.jsx)(n.a,{href:"https://apple.github.io/foundationdb/getting-started-linux.html",children:"FoundationDB client cli"})," which is used to communicate with the FoundationDB cluster."]}),"\n",(0,i.jsxs)(n.li,{children:["Install the ",(0,i.jsx)(n.a,{href:"https://www.npmjs.com/package/foundationdb",children:"FoundationDB node bindings npm module"})," via ",(0,i.jsx)(n.code,{children:"npm install foundationdb"}),". This will install ",(0,i.jsx)(n.code,{children:"v2.x.x"}),", which is only compatible with FoundationDB server and client ",(0,i.jsx)(n.code,{children:"v7.3.x"})," (which is the only version currently maintained by the FoundationDB team). If you need to use an older version (e.g. ",(0,i.jsx)(n.code,{children:"7.1.x"})," or ",(0,i.jsx)(n.code,{children:"6.3.x"}),"), you should run ",(0,i.jsx)(n.code,{children:"npm install foundationdb@1.1.4"})," (though this might only work with ",(0,i.jsx)(n.code,{children:"v6.3.x"}),")."]}),"\n",(0,i.jsxs)(n.li,{children:["Due to an outstanding bug in node foundationdb, you will need to specify an ",(0,i.jsx)(n.code,{children:"apiVersion"})," of ",(0,i.jsx)(n.code,{children:"720"})," even though you are using ",(0,i.jsx)(n.code,{children:"730"}),". When ",(0,i.jsx)(n.a,{href:"https://github.com/josephg/node-foundationdb/pull/86",children:"this PR"})," is merged, you will be able to use ",(0,i.jsx)(n.code,{children:"730"}),"."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageFoundationDB"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-foundationdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageFoundationDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Version of the API of the FoundationDB cluster.."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * FoundationDB is backwards compatible across a wide range of versions,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * so you have to specify the api version."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If in doubt, set it to 720."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" apiVersion"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 720"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Path to the FoundationDB cluster file."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If in doubt, leave this empty to use the default location."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" clusterFile"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/path/to/fdb.cluster'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Amount of documents to be fetched in batch requests."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can change this to improve performance depending on"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * your database access patterns."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=50]"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"multi-instance",children:"Multi Instance"}),"\n",(0,i.jsxs)(n.p,{children:["Because FoundationDB does not offer a ",(0,i.jsx)(n.a,{href:"https://forums.foundationdb.org/t/streaming-data-out-of-foundationdb/683/2",children:"changestream"}),", it is not possible to use the same cluster from more than one Node.js process at the same time. For example you cannot spin up multiple servers with RxDB databases that all use the same cluster. There might be workarounds to create something like a FoundationDB changestream and you can make a Pull Request if you need that feature."]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}},8453(e,n,s){s.d(n,{R:()=>t,x:()=>r});var a=s(6540);const i={},o=a.createContext(i);function t(e){const n=a.useContext(o);return a.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:t(e.components),a.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/714575d7.f4b12fd2.js b/docs/assets/js/714575d7.f4b12fd2.js
deleted file mode 100644
index 4fa922cee2c..00000000000
--- a/docs/assets/js/714575d7.f4b12fd2.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3185],{924(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>i,default:()=>h,frontMatter:()=>l,metadata:()=>o,toc:()=>c});const o=JSON.parse('{"id":"rx-local-document","title":"Master Local Documents in RxDB","description":"Effortlessly store custom metadata and app settings in RxDB. Learn how Local Documents keep data flexible, secure, and easy to manage.","source":"@site/docs/rx-local-document.md","sourceDirName":".","slug":"/rx-local-document.html","permalink":"/rx-local-document.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Master Local Documents in RxDB","slug":"rx-local-document.html","description":"Effortlessly store custom metadata and app settings in RxDB. Learn how Local Documents keep data flexible, secure, and easy to manage."},"sidebar":"tutorialSidebar","previous":{"title":"RxState","permalink":"/rx-state.html"},"next":{"title":"Cleanup","permalink":"/cleanup.html"}}');var r=n(4848),a=n(8453);const l={title:"Master Local Documents in RxDB",slug:"rx-local-document.html",description:"Effortlessly store custom metadata and app settings in RxDB. Learn how Local Documents keep data flexible, secure, and easy to manage."},i="Local Documents",t={},c=[{value:"Add the local documents plugin",id:"add-the-local-documents-plugin",level:2},{value:"Activate the plugin for a RxDatabase or RxCollection",id:"activate-the-plugin-for-a-rxdatabase-or-rxcollection",level:2},{value:"insertLocal()",id:"insertlocal",level:2},{value:"upsertLocal()",id:"upsertlocal",level:2},{value:"getLocal()",id:"getlocal",level:2},{value:"getLocal$()",id:"getlocal-1",level:2},{value:"RxLocalDocument",id:"rxlocaldocument",level:2}];function d(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(s.header,{children:(0,r.jsx)(s.h1,{id:"local-documents",children:"Local Documents"})}),"\n",(0,r.jsx)(s.p,{children:"Local documents are a special class of documents which are used to store local metadata.\nThey come in handy when you want to store settings or additional data next to your documents."}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["Local Documents can exist on a ",(0,r.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"})," or ",(0,r.jsx)(s.a,{href:"/rx-collection.html",children:"RxCollection"}),"."]}),"\n",(0,r.jsx)(s.li,{children:"Local Document do not have to match the collections schema."}),"\n",(0,r.jsx)(s.li,{children:"Local Documents do not get replicated."}),"\n",(0,r.jsx)(s.li,{children:"Local Documents will not be found on queries."}),"\n",(0,r.jsx)(s.li,{children:"Local Documents can not have attachments."}),"\n",(0,r.jsxs)(s.li,{children:["Local Documents will not get handled by the ",(0,r.jsx)(s.a,{href:"/migration-schema.html",children:"migration-schema"}),"."]}),"\n",(0,r.jsxs)(s.li,{children:["The id of a local document has the ",(0,r.jsx)(s.code,{children:"maxLength"})," of ",(0,r.jsx)(s.code,{children:"128"})," characters."]}),"\n"]}),"\n",(0,r.jsx)(s.admonition,{type:"note",children:(0,r.jsxs)(s.p,{children:["While local documents can be very useful, in many cases the ",(0,r.jsx)(s.a,{href:"/rx-state.html",children:"RxState"})," API is more convenient."]})}),"\n",(0,r.jsx)(s.h2,{id:"add-the-local-documents-plugin",children:"Add the local documents plugin"}),"\n",(0,r.jsxs)(s.p,{children:["To enable the local documents, you have to add the ",(0,r.jsx)(s.code,{children:"local-documents"})," plugin."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBLocalDocumentsPlugin } "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/local-documents'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBLocalDocumentsPlugin);"})]})]})})}),"\n",(0,r.jsx)(s.h2,{id:"activate-the-plugin-for-a-rxdatabase-or-rxcollection",children:"Activate the plugin for a RxDatabase or RxCollection"}),"\n",(0,r.jsxs)(s.p,{children:["For better performance, the local document plugin does not create a storage for every database or collection that is created.\nInstead you have to set ",(0,r.jsx)(s.code,{children:"localDocuments: true"})," when you want to store local documents in the instance."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// activate local documents on a RxDatabase"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" localDocuments"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- activate this to store local documents in the database"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDatabase"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" messages"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" messageSchema"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" localDocuments"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- activate this to store local documents in the collection"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,r.jsx)(s.admonition,{type:"note",children:(0,r.jsxs)(s.p,{children:["If you want to store local documents in a ",(0,r.jsx)(s.code,{children:"RxCollection"})," but ",(0,r.jsx)(s.strong,{children:"NOT"})," in the ",(0,r.jsx)(s.code,{children:"RxDatabase"}),", you ",(0,r.jsx)(s.strong,{children:"MUST NOT"})," set ",(0,r.jsx)(s.code,{children:"localDocuments: true"})," in the ",(0,r.jsx)(s.code,{children:"RxDatabase"})," because it will only slow down the initial database creation."]})}),"\n",(0,r.jsx)(s.h2,{id:"insertlocal",children:"insertLocal()"}),"\n",(0,r.jsxs)(s.p,{children:["Creates a local document for the database or collection. Throws if a local document with the same id already exists. Returns a Promise which resolves the new ",(0,r.jsx)(s.code,{children:"RxLocalDocument"}),"."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insertLocal"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // id"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// data"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// you can also use local-documents on a database"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insertLocal"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // id"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// data"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,r.jsx)(s.h2,{id:"upsertlocal",children:"upsertLocal()"}),"\n",(0,r.jsxs)(s.p,{children:["Creates a local document for the database or collection if not exists. Overwrites the if exists. Returns a Promise which resolves the ",(0,r.jsx)(s.code,{children:"RxLocalDocument"}),"."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".upsertLocal"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // id"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// data"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,r.jsx)(s.h2,{id:"getlocal",children:"getLocal()"}),"\n",(0,r.jsxs)(s.p,{children:["Find a ",(0,r.jsx)(s.code,{children:"RxLocalDocument"})," by its id. Returns a Promise which resolves the ",(0,r.jsx)(s.code,{children:"RxLocalDocument"})," or ",(0,r.jsx)(s.code,{children:"null"})," if not exists."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,r.jsx)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getLocal"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})})})}),"\n",(0,r.jsx)(s.h2,{id:"getlocal-1",children:"getLocal$()"}),"\n",(0,r.jsxs)(s.p,{children:["Like ",(0,r.jsx)(s.code,{children:"getLocal()"})," but returns an ",(0,r.jsx)(s.code,{children:"Observable"})," that emits the document or ",(0,r.jsx)(s.code,{children:"null"})," if not exists."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" subscription"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getLocal$"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(documentOrNull "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(documentOrNull); "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > RxLocalDocument or null"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,r.jsx)(s.h2,{id:"rxlocaldocument",children:"RxLocalDocument"}),"\n",(0,r.jsxs)(s.p,{children:["A ",(0,r.jsx)(s.code,{children:"RxLocalDocument"})," behaves like a normal ",(0,r.jsx)(s.code,{children:"RxDocument"}),"."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getLocal"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// access data"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foo'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// change data"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".set"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foo'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar2'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".save"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// observe data"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get$"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foo'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(value "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* .. */"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// remove it"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,r.jsx)(s.admonition,{type:"note",children:(0,r.jsx)(s.p,{children:"Because the local document does not have a schema, accessing the documents data-fields via pseudo-proxy will not work."})}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".foo; "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// undefined"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foo'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"); "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// works!"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".foo "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"; "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// does not work!"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".set"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foo'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"); "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// works"})]})]})})}),"\n",(0,r.jsxs)(s.p,{children:["For the usage with typescript, you can have access to the typed data of the document over ",(0,r.jsx)(s.code,{children:"toJSON()"})]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"declare"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" type"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" MyLocalDocumentType"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" string"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".upsertLocal"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"MyLocalDocumentType"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">("})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // id"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// data"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// typescript will know that foo is a string"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" foo"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" localDoc"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".toJSON"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().foo;"})]})]})})})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,r.jsx)(s,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>l,x:()=>i});var o=n(6540);const r={},a=o.createContext(r);function l(e){const s=o.useContext(a);return o.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function i(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:l(e.components),o.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/7443.f0d813bb.js b/docs/assets/js/7443.f0d813bb.js
deleted file mode 100644
index 955917214af..00000000000
--- a/docs/assets/js/7443.f0d813bb.js
+++ /dev/null
@@ -1 +0,0 @@
-(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7443],{4004(e,t,s){"use strict";s.r(t),s.d(t,{default:()=>y});var i=s(4714),n=s.n(i);const l=s(8291);l.tokenizer.separator=/[\s\-/]+/;const a=class{constructor(e,t,s="/",i){this.searchDocs=e,this.lunrIndex=l.Index.load(t),this.baseUrl=s,this.maxHits=i}getLunrResult(e){return this.lunrIndex.query(function(t){const s=l.tokenizer(e);t.term(s,{boost:10}),t.term(s,{wildcard:l.Query.wildcard.TRAILING})})}getHit(e,t,s){return{hierarchy:{lvl0:e.pageTitle||e.title,lvl1:0===e.type?null:e.title},url:e.url,version:e.version,_snippetResult:s?{content:{value:s,matchLevel:"full"}}:null,_highlightResult:{hierarchy:{lvl0:{value:0===e.type?t||e.title:e.pageTitle},lvl1:0===e.type?null:{value:t||e.title}}}}}getTitleHit(e,t,s){const i=t[0],n=t[0]+s;let l=e.title.substring(0,i)+''+e.title.substring(i,n)+""+e.title.substring(n,e.title.length);return this.getHit(e,l)}getKeywordHit(e,t,s){const i=t[0],n=t[0]+s;let l=e.title+" Keywords: "+e.keywords.substring(0,i)+''+e.keywords.substring(i,n)+""+e.keywords.substring(n,e.keywords.length)+"";return this.getHit(e,l)}getContentHit(e,t){const s=t[0],i=t[0]+t[1];let n=s,l=i,a=!0,r=!0;for(let c=0;c<3;c++){const t=e.content.lastIndexOf(" ",n-2),s=e.content.lastIndexOf(".",n-2);if(s>0&&s>t){n=s+1,a=!1;break}if(t<0){n=0,a=!1;break}n=t+1}for(let c=0;c<10;c++){const t=e.content.indexOf(" ",l+1),s=e.content.indexOf(".",l+1);if(s>0&&s'+e.content.substring(s,i)+"",o+=e.content.substring(i,l),r&&(o+=" ..."),this.getHit(e,null,o)}search(e){return new Promise((t,s)=>{const i=this.getLunrResult(e),n=[];i.length>this.maxHits&&(i.length=this.maxHits),this.titleHitsRes=[],this.contentHitsRes=[],i.forEach(t=>{const s=this.searchDocs[t.ref],{metadata:i}=t.matchData;for(let l in i)if(i[l].title){if(!this.titleHitsRes.includes(t.ref)){const a=i[l].title.position[0];n.push(this.getTitleHit(s,a,e.length)),this.titleHitsRes.push(t.ref)}}else if(i[l].content){const e=i[l].content.position[0];n.push(this.getContentHit(s,e))}else if(i[l].keywords){const a=i[l].keywords.position[0];n.push(this.getKeywordHit(s,a,e.length)),this.titleHitsRes.push(t.ref)}}),n.length>this.maxHits&&(n.length=this.maxHits),t(n)})}};var r=s(4498),o=s.n(r);const c="algolia-docsearch",h=`${c}-suggestion`,u={suggestion:`\n \n
\n '};var g=s(3704),d=s.n(g);const p={mergeKeyWithParent(e,t){if(void 0===e[t])return e;if("object"!=typeof e[t])return e;const s=d().extend({},e,e[t]);return delete s[t],s},groupBy(e,t){const s={};return d().each(e,(e,i)=>{if(void 0===i[t])throw new Error(`[groupBy]: Object has no key ${t}`);let n=i[t];"string"==typeof n&&(n=n.toLowerCase()),Object.prototype.hasOwnProperty.call(s,n)||(s[n]=[]),s[n].push(i)}),s},values:e=>Object.keys(e).map(t=>e[t]),flatten(e){const t=[];return e.forEach(e=>{Array.isArray(e)?e.forEach(e=>{t.push(e)}):t.push(e)}),t},flattenAndFlagFirst(e,t){const s=this.values(e).map(e=>e.map((e,s)=>(e[t]=0===s,e)));return this.flatten(s)},compact(e){const t=[];return e.forEach(e=>{e&&t.push(e)}),t},getHighlightedValue:(e,t)=>e._highlightResult&&e._highlightResult.hierarchy_camel&&e._highlightResult.hierarchy_camel[t]&&e._highlightResult.hierarchy_camel[t].matchLevel&&"none"!==e._highlightResult.hierarchy_camel[t].matchLevel&&e._highlightResult.hierarchy_camel[t].value?e._highlightResult.hierarchy_camel[t].value:e._highlightResult&&e._highlightResult&&e._highlightResult[t]&&e._highlightResult[t].value?e._highlightResult[t].value:e[t],getSnippetedValue(e,t){if(!e._snippetResult||!e._snippetResult[t]||!e._snippetResult[t].value)return e[t];let s=e._snippetResult[t].value;return s[0]!==s[0].toUpperCase()&&(s=`\u2026${s}`),-1===[".","!","?"].indexOf(s[s.length-1])&&(s=`${s}\u2026`),s},deepClone:e=>JSON.parse(JSON.stringify(e))};class v{constructor({searchDocs:e,searchIndex:t,inputSelector:s,debug:i=!1,baseUrl:n="/",queryDataCallback:l=null,autocompleteOptions:r={debug:!1,hint:!1,autoselect:!0},transformData:c=!1,queryHook:h=!1,handleSelected:g=!1,enhancedSearchInput:p=!1,layout:y="column",maxHits:m=5}){this.input=v.getInputFromSelector(s),this.queryDataCallback=l||null;const b=!(!r||!r.debug)&&r.debug;r.debug=i||b,this.autocompleteOptions=r,this.autocompleteOptions.cssClasses=this.autocompleteOptions.cssClasses||{},this.autocompleteOptions.cssClasses.prefix=this.autocompleteOptions.cssClasses.prefix||"ds";const f=this.input&&"function"==typeof this.input.attr&&this.input.attr("aria-label");this.autocompleteOptions.ariaLabel=this.autocompleteOptions.ariaLabel||f||"search input",this.isSimpleLayout="simple"===y,this.client=new a(e,t,n,m),p&&(this.input=v.injectSearchBox(this.input)),this.autocomplete=o()(this.input,r,[{source:this.getAutocompleteSource(c,h),templates:{suggestion:v.getSuggestionTemplate(this.isSimpleLayout),footer:u.footer,empty:v.getEmptyTemplate()}}]);const x=g;this.handleSelected=x||this.handleSelected,x&&d()(".algolia-autocomplete").on("click",".ds-suggestions a",e=>{e.preventDefault()}),this.autocomplete.on("autocomplete:selected",this.handleSelected.bind(null,this.autocomplete.autocomplete)),this.autocomplete.on("autocomplete:shown",this.handleShown.bind(null,this.input)),p&&v.bindSearchBoxEvent(),document.addEventListener("keydown",e=>{(e.ctrlKey||e.metaKey)&&"k"==e.key&&(this.input.focus(),e.preventDefault())})}static injectSearchBox(e){e.before(u.searchBox);const t=e.prev().prev().find("input");return e.remove(),t}static bindSearchBoxEvent(){d()('.searchbox [type="reset"]').on("click",function(){d()("input#docsearch").focus(),d()(this).addClass("hide"),o().autocomplete.setVal("")}),d()("input#docsearch").on("keyup",()=>{const e=document.querySelector("input#docsearch"),t=document.querySelector('.searchbox [type="reset"]');t.className="searchbox__reset",0===e.value.length&&(t.className+=" hide")})}static getInputFromSelector(e){const t=d()(e).filter("input");return t.length?d()(t[0]):null}getAutocompleteSource(e,t){return(s,i)=>{t&&(s=t(s)||s),this.client.search(s).then(t=>{this.queryDataCallback&&"function"==typeof this.queryDataCallback&&this.queryDataCallback(t),e&&(t=e(t)||t),i(v.formatHits(t))})}}static formatHits(e){const t=p.deepClone(e).map(e=>(e._highlightResult&&(e._highlightResult=p.mergeKeyWithParent(e._highlightResult,"hierarchy")),p.mergeKeyWithParent(e,"hierarchy")));let s=p.groupBy(t,"lvl0");return d().each(s,(e,t)=>{const i=p.groupBy(t,"lvl1"),n=p.flattenAndFlagFirst(i,"isSubCategoryHeader");s[e]=n}),s=p.flattenAndFlagFirst(s,"isCategoryHeader"),s.map(e=>{const t=v.formatURL(e),s=p.getHighlightedValue(e,"lvl0"),i=p.getHighlightedValue(e,"lvl1")||s,n=p.compact([p.getHighlightedValue(e,"lvl2")||i,p.getHighlightedValue(e,"lvl3"),p.getHighlightedValue(e,"lvl4"),p.getHighlightedValue(e,"lvl5"),p.getHighlightedValue(e,"lvl6")]).join(' \u203a '),l=p.getSnippetedValue(e,"content"),a=i&&""!==i||n&&""!==n,r=!i||""===i||i===s,o=n&&""!==n&&n!==i,c=!o&&i&&""!==i&&i!==s,h=!c&&!o,u=e.version;return{isLvl0:h,isLvl1:c,isLvl2:o,isLvl1EmptyOrDuplicate:r,isCategoryHeader:e.isCategoryHeader,isSubCategoryHeader:e.isSubCategoryHeader,isTextOrSubcategoryNonEmpty:a,category:s,subcategory:i,title:n,text:l,url:t,version:u}})}static formatURL(e){const{url:t,anchor:s}=e;if(t){return-1!==t.indexOf("#")?t:s?`${e.url}#${e.anchor}`:t}return s?`#${e.anchor}`:(console.warn("no anchor nor url for : ",JSON.stringify(e)),null)}static getEmptyTemplate(){return e=>n().compile(u.empty).render(e)}static getSuggestionTemplate(e){const t=e?u.suggestionSimple:u.suggestion,s=n().compile(t);return e=>s.render(e)}handleSelected(e,t,s,i,n={}){"click"!==n.selectionMethod&&(e.setVal(""),window.location.assign(s.url))}handleShown(e){const t=e.offset().left+e.width()/2;let s=d()(document).width()/2;isNaN(s)&&(s=900);const i=t-s>=0?"algolia-autocomplete-right":"algolia-autocomplete-left",n=t-s<0?"algolia-autocomplete-right":"algolia-autocomplete-left",l=d()(".algolia-autocomplete");l.hasClass(i)||l.addClass(i),l.hasClass(n)&&l.removeClass(n)}}const y=v},5741(){}}]);
\ No newline at end of file
diff --git a/docs/assets/js/77979bef.e414ae82.js b/docs/assets/js/77979bef.e414ae82.js
deleted file mode 100644
index bb389451007..00000000000
--- a/docs/assets/js/77979bef.e414ae82.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5861],{2443(e,s,t){t.d(s,{G:()=>a});t(6540);var n=t(8853),i=t(4848);function a({author:e,year:s,sourceLink:t,children:a}){return(0,i.jsxs)("div",{style:{borderLeft:"2px solid var(--color-top)",paddingLeft:"1rem",paddingTop:"0.5rem",paddingBottom:"0.5rem",marginTop:30,marginBottom:30},children:[(0,i.jsx)(n.iG,{}),(0,i.jsx)("div",{style:{display:"flex",alignItems:"flex-start",gap:"0.5rem"},children:(0,i.jsx)("p",{style:{margin:0},children:a})}),(0,i.jsx)("div",{style:{display:"flex",justifyContent:"flex-end",gap:5},children:(0,i.jsx)(n.fz,{})}),(0,i.jsxs)("p",{style:{marginTop:"0.75rem",marginBottom:0,textAlign:"right",fontSize:"0.9rem"},children:["\u2013"," ",t?(0,i.jsx)("a",{href:t,target:"_blank",rel:"noopener noreferrer",children:e}):e,s&&`, ${s}`]})]})}},3247(e,s,t){t.d(s,{g:()=>i});var n=t(4848);function i(e){const s=[];let t=null;return e.children.forEach(e=>{e.props.id?(t&&s.push(t),t={headline:e,paragraphs:[]}):t&&t.paragraphs.push(e)}),t&&s.push(t),(0,n.jsx)("div",{style:a.stepsContainer,children:s.map((e,s)=>(0,n.jsxs)("div",{style:a.stepWrapper,children:[(0,n.jsxs)("div",{style:a.stepIndicator,children:[(0,n.jsx)("div",{style:a.stepNumber,children:s+1}),(0,n.jsx)("div",{style:a.verticalLine})]}),(0,n.jsxs)("div",{style:a.stepContent,children:[(0,n.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,n.jsx)("div",{style:a.item,children:e},s))]})]},s))})}const a={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},4113(e,s,t){t.r(s),t.d(s,{assets:()=>d,contentTitle:()=>c,default:()=>u,frontMatter:()=>l,metadata:()=>n,toc:()=>h});const n=JSON.parse('{"id":"articles/local-first-future","title":"Why Local-First Software Is the Future and its Limitations","description":"Discover how local-first transforms web apps, boosts offline resilience, and why instant user feedback is becoming the new normal.","source":"@site/docs/articles/local-first-future.md","sourceDirName":"articles","slug":"/articles/local-first-future.html","permalink":"/articles/local-first-future.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Why Local-First Software Is the Future and its Limitations","slug":"local-first-future.html","description":"Discover how local-first transforms web apps, boosts offline resilience, and why instant user feedback is becoming the new normal."},"sidebar":"tutorialSidebar","previous":{"title":"Benefits of RxDB & Browser Databases","permalink":"/articles/browser-database.html"},"next":{"title":"Why NoSQL Powers Modern UI Apps","permalink":"/why-nosql.html"}}');var i=t(4848),a=t(8453),r=(t(2636),t(3247),t(2443)),o=t(2271);const l={title:"Why Local-First Software Is the Future and its Limitations",slug:"local-first-future.html",description:"Discover how local-first transforms web apps, boosts offline resilience, and why instant user feedback is becoming the new normal."},c="Why Local-First Software Is the Future and what are its Limitations",d={},h=[{value:"What is the Local-First Paradigm",id:"what-is-the-local-first-paradigm",level:2},{value:"Why Local-First is Gaining Traction",id:"why-local-first-is-gaining-traction",level:2},{value:"What you can expect from a Local First App",id:"what-you-can-expect-from-a-local-first-app",level:2},{value:"User Experience Benefits",id:"user-experience-benefits",level:3},{value:"Developer Experience Benefits",id:"developer-experience-benefits",level:3},{value:"Challenges and Limitations of Local-First",id:"challenges-and-limitations-of-local-first",level:2},{value:"Local-First vs. Traditional Online-First Approaches",id:"local-first-vs-traditional-online-first-approaches",level:2},{value:"Connectivity and Offline Usage",id:"connectivity-and-offline-usage",level:3},{value:"Latency and Performance",id:"latency-and-performance",level:3},{value:"Complexity and Conflict Resolution",id:"complexity-and-conflict-resolution",level:3},{value:"Data Ownership and Storage Limits",id:"data-ownership-and-storage-limits",level:3},{value:"When to Choose Which",id:"when-to-choose-which",level:3},{value:"Offline-First vs. Local-First",id:"offline-first-vs-local-first",level:2},{value:"Do People Actually Use Local-First or Is It Just a Trend?",id:"do-people-actually-use-local-first-or-is-it-just-a-trend",level:2},{value:"Why Local-First Is the Future",id:"why-local-first-is-the-future",level:2},{value:"See also",id:"see-also",level:2}];function p(e){const s={a:"a",blockquote:"blockquote",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"why-local-first-software-is-the-future-and-what-are-its-limitations",children:"Why Local-First Software Is the Future and what are its Limitations"})}),"\n",(0,i.jsxs)(s.p,{children:["Imagine a web app that behaves seamlessly even with zero internet access, provides sub-millisecond response times, and keeps most of the user's data on their device. This is the ",(0,i.jsx)(s.strong,{children:"local-first"})," or ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"offline-first"})," approach. Although it has been around for a while, local-first has recently become more practical because of ",(0,i.jsx)(s.strong,{children:"maturing browser storage APIs"})," and new frameworks that simplify ",(0,i.jsx)(s.strong,{children:"data synchronization"}),". By allowing data to live on the client and only syncing with a server or other peers when needed, local-first apps can deliver a user experience that is ",(0,i.jsx)(s.strong,{children:"fast, resilient"}),", and ",(0,i.jsx)(s.strong,{children:"privacy-friendly"}),"."]}),"\n",(0,i.jsx)(s.p,{children:"However, local-first is no silver bullet. It introduces tricky distributed-data challenges like conflict resolution and schema migrations on client devices. In this article, we'll dive deep into what local-first means, why it's trending, its pros and cons, and how to implement it in real applications. We'll also discuss other tools, criticisms, backend considerations, and how local-first compares to traditional cloud-centric approaches."}),"\n",(0,i.jsx)(s.h2,{id:"what-is-the-local-first-paradigm",children:"What is the Local-First Paradigm"}),"\n",(0,i.jsxs)(s.p,{children:["In ",(0,i.jsx)(s.strong,{children:"local-first"})," software, the primary copy of your data lives on the ",(0,i.jsx)(s.strong,{children:"client"})," rather than a remote server. Rather than sending each read or write over the network, you store and manipulate data in a ",(0,i.jsx)(s.a,{href:"/articles/local-database.html",children:"local database"})," on the user\u2019s device. Sync then happens in the background, ensuring all devices eventually converge to a consistent state."]}),"\n",(0,i.jsxs)(s.p,{children:["This approach is increasingly popular because it leads to ",(0,i.jsx)(s.strong,{children:"instant"})," app responses (no network delay for most operations), genuine ",(0,i.jsx)(s.strong,{children:"offline capability"}),", and more direct ",(0,i.jsx)(s.strong,{children:"data ownership"})," for users. Local-first apps also sidestep outages and if the server or internet goes down, users can keep working with their local data. When connectivity returns, everything syncs. This makes the user experience ",(0,i.jsx)(s.strong,{children:"more resilient"})," and gives them control of their data, which is especially appealing when privacy concerns or limited connectivity are key factors."]}),"\n",(0,i.jsx)(r.G,{author:"Ink&Switch",year:"2019",sourceLink:"https://martin.kleppmann.com/papers/local-first.pdf",children:"Local-First software: A set of principles for software that enables both collaboration and ownership for users. Local-first ideals include the ability to work offline and collaborate across multiple devices, while also improving the security, privacy, long-term preservation, and user control of data."}),"\n",(0,i.jsx)(s.h2,{id:"why-local-first-is-gaining-traction",children:"Why Local-First is Gaining Traction"}),"\n",(0,i.jsx)(s.p,{children:"The push for local-first is driven by a few key new technological capabilities that previously restricted client devices from running heavy local-first computing:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Relaxed Browser Storage Limits"}),": In the past, true local-first web apps were not very feasible due to ",(0,i.jsx)(s.strong,{children:"storage limitations"})," in browsers. Early web storage options like cookies or ",(0,i.jsx)(s.a,{href:"/articles/localstorage.html#understanding-the-limitations-of-local-storage",children:"localStorage"})," had tiny limits (~5-10MB) and were unsuitable for complex data. Even ",(0,i.jsx)(s.strong,{children:"IndexedDB"}),", the structured client storage introduced over a decade ago, had restrictive quotas on many browsers: For example, older Firefox versions would ",(0,i.jsx)(s.strong,{children:"prompt the user if more than 50MB"})," was being stored. Mobile browsers often capped IndexedDB to 5MB without user permission. Such limits made it impractical to cache large application datasets on the client. However, modern browsers have dramatically ",(0,i.jsx)(s.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"increased these limits"}),". Today, IndexedDB can typically store ",(0,i.jsx)(s.strong,{children:"hundreds of megabytes to multiple gigabytes"})," of data, depending on device capacity. Chrome allows up to ~80% of free disk space per origin (tens of GB on a desktop), Firefox now supports on the order of gigabytes per site (10% of disk size), and even Safari (historically strict) permits around 1GB per origin on iOS. In short, the storage quotas of 5-50MB are a thing of the past and modern web apps can cache very large datasets locally without hitting a ceiling. This shift in storage capabilities has unlocked new possibilities for ",(0,i.jsx)(s.strong,{children:"local-first web apps"})," that simply weren't viable a few years ago."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"New Storage APIs (OPFS)"}),": The new Browser API ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"Origin Private File System"})," (OPFS), part of the File System Access API, enables near-native file I/O from within a browser. It allows web apps to manage file handles securely and perform fast, synchronous reads/writes in Web Workers. This is a huge deal for local-first computing because it makes it feasible to embed robust database engines directly in the browser, persisting data to real files on a virtual filesystem. With OPFS, you can avoid some of the performance overhead that comes with ",(0,i.jsx)(s.a,{href:"/slow-indexeddb.html",children:"IndexedDB-based workarounds"}),", providing a near-native ",(0,i.jsx)(s.a,{href:"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html#big-bulk-writes",children:"speed experience"})," for file-structured data."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)("div",{style:{textAlign:"justify"},children:[(0,i.jsx)("img",{src:"/files/latency-london-san-franzisco.png",alt:"latency london san franzisco",width:"300",className:"img-in-text-right"}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Bandwidth Has Grown, But Latency Is Capped"}),": Internet infrastructure has rapidly expanded to provide higher throughput making it possible to transfer large amounts of data more quickly. However, latency (i.e., round-trip delay) is constrained by the ",(0,i.jsx)(s.strong,{children:"speed of light"})," and other physical limitations in fiber, satellite links, and routing. We can always build out bigger \"pipes\" to stream or send bulk data, but we can't significantly reduce the base round-trip time for each request. This is a physical limit, not a technological one. Local-first strategies mitigate this fundamental latency limit by avoiding excessive client-server calls in interactive workflows, once data is on the client, it's instantly available for reads and writes without waiting on a network round-trip. Imagine, transferring ",(0,i.jsx)(s.strong,{children:"around 100,000"}),' "average" JSON documents might only consume ',(0,i.jsx)(s.strong,{children:"about the same bandwidth as two frames of a 4K YouTube video"})," which can be transferred in milliseconds. This shows just how far raw data throughput has come. Yet each request still has a 100-200ms latency or more, which becomes noticeable in user interactions. Local-first mitigates this by minimizing round-trip calls during active use and using the available bandwidth to directly transfer most of the data on the first app start."]}),"\n"]})]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"WebAssembly"}),": Another advancement is ",(0,i.jsx)(s.strong,{children:"WebAssembly (WASM)"}),", which allows developers to compile low-level languages (C, C++, Rust) for execution in the browser at near-native speed. This means database engines, search algorithms, ",(0,i.jsx)(s.a,{href:"/articles/javascript-vector-database.html",children:"vector databases"}),", and other performance-heavy tasks can run right on the client. However, a key limitation is that ",(0,i.jsx)(s.strong,{children:"WASM cannot directly access persistent storage APIs"})," in the browser. Instead, all data must be send from WASM to JavaScript (or the main thread) and then go through something like IndexedDB or OPFS. This extra indirection ",(0,i.jsx)(s.a,{href:"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html",children:"is slower"})," compared to plain JavaScript->storage calls. Looking ahead, there might come up future APIs that allow WASM to interface with persistent storage directly, and if those land, local-first systems could see another major boost in ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Improvements in Local-First Tooling"}),": A major factor fueling the rise of local-first architectures is the ",(0,i.jsx)(s.strong,{children:"dramatic leap in client-side tooling and performance"}),". For instance, consider a local-first ",(0,i.jsx)(s.strong,{children:"email client"})," that stores ",(0,i.jsx)(s.strong,{children:"one million messages"}),". In 2014, searching through that many documents, especially with something like early PouchDB, could take ",(0,i.jsx)(s.strong,{children:"minutes"})," in a browser. Today, with advanced offline databases like ",(0,i.jsx)(s.strong,{children:"RxDB"}),", you can use the ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS storage"})," with ",(0,i.jsx)(s.a,{href:"/rx-storage-sharding.html",children:"sharding"})," across multiple ",(0,i.jsx)(s.a,{href:"/rx-storage-worker.html",children:"web workers"})," (one per CPU) and use ",(0,i.jsx)(s.a,{href:"/rx-storage-memory-mapped.html",children:"memory-mapped"})," techniques. The result is a ",(0,i.jsx)(s.strong,{children:"regex search"})," of one million of these email documents in around ",(0,i.jsx)(s.strong,{children:"120 milliseconds"})," - all in JavaScript, running inside a standard web browser, on a mobile phone."]}),"\n",(0,i.jsxs)(s.p,{children:["Better yet, this performance ceiling is likely to keep rising. Newer browser features and ",(0,i.jsx)(s.strong,{children:"WebAssembly"})," optimizations could enable even faster indexing and query operations, closing the gap with native desktop clients. I even experimented with GPU-accelarated queries (using ",(0,i.jsx)(s.strong,{children:"WebGPU"}),") which, while still in experimental stage, might deliver client-side performance that outperforms servers which do not have a graphics card.\nThese transformations highlight why local-first has become truly practical: not only can you sync and work offline, but you can handle ",(0,i.jsx)(s.strong,{children:"serious data loads"})," with performance that would have been unthinkable just a few years ago."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"what-you-can-expect-from-a-local-first-app",children:"What you can expect from a Local First App"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Jevons_paradox",children:"Jevons' Paradox"})," says that making a ",(0,i.jsx)(s.em,{children:"resource cheaper or more efficient to use often leads to greater overall consumption"}),". Originally about coal, it applies to the local-first paradigm in a way where we require apps to have more features, simply because it is technically possible, the app users and developers start to expect them:"]}),"\n",(0,i.jsx)(s.h3,{id:"user-experience-benefits",children:"User Experience Benefits"}),"\n",(0,i.jsxs)("div",{style:{textAlign:"justify"},children:[(0,i.jsx)("img",{src:"/files/loading-spinner-not-needed.gif",alt:"loading spinner not needed",width:"160",className:"img-in-text-right"}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Performance & UX:"})," Running from local storage means ",(0,i.jsx)(s.strong,{children:"low latency"})," and instantaneous interactions. There's no round-trip delay for most operations. Local-first apps aim to provide ",(0,i.jsx)(s.a,{href:"/articles/zero-latency-local-first.html",children:"near-zero latency"})," responses by querying a local database instead of waiting for a server response\u200b. This results in a snappy UX (often no need for loading spinners) because data reads/writes happen immediately on-device. Modern users expect real-time feedback, and local-first delivers that by default."]}),"\n"]})]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"User Control & Privacy:"})," Storing data locally can limit how much sensitive information is sent off to remote servers. End users have greater control over their data, and the app can implement ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"client-side encryption"}),", thereby reducing the risk of mass data breaches. Its even possible to only replicated encrypted data with a server so that the backend does not know about the data at all and just acts as a backup/replication endpoint."]}),"\n"]}),"\n",(0,i.jsxs)("div",{style:{textAlign:"justify"},children:[(0,i.jsx)("img",{src:"/files/offline-ready.png",alt:"offline ready",width:"110",className:"img-in-text-right"}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline Resilience:"})," Obviously, being able to work offline is a major benefit. Users can continue using the app with no internet (or flaky connectivity), and their changes sync up once online. This is increasingly important not just for remote areas, but for any app that needs to be available 24/7. Even though mobile networks have improved, connectivity can still drop; local-first ensures the app doesn't grind to a halt. The app ",(0,i.jsx)(s.em,{children:'"stores data locally at the client so that it can still access it when the internet goes away."'})]}),"\n"]})]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Realtime Apps"}),": Today's users expect data to stay in sync across browser tabs and devices without constant page reloads. In a typical cloud app, if you want real-time updates (say to show that a friend edited a document), you'd need to implement a ",(0,i.jsx)(s.a,{href:"/articles/websockets-sse-polling-webrtc-webtransport.html",children:"websocket or polling"})," system for the server to push changes to clients, which is complex. Local-first architectures naturally lend themselves to realtime-by-default updates because the application state lives in a local database that can be observed for changes. Any edits (local or incoming from the server) immediately trigger ",(0,i.jsx)(s.a,{href:"/articles/optimistic-ui.html",children:"UI updates"}),". Similarly, background sync mechanisms ensure that new server-side data flows into the local store and into the user interface right away, no need to hit F5 to fetch the latest changes like on a traditional webpage."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"/files/animations/realtime.gif",alt:"realtime ui updates",width:"700",className:"img-radius"})}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"developer-experience-benefits",children:"Developer Experience Benefits"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Reduced Server Load"}),": Because local-first architectures typically ",(0,i.jsx)(s.strong,{children:"transfer large chunks of data once"})," (e.g., during an initial sync) and then sync only small diffs (delta changes) afterward, the server does not have to handle repeated requests for the same dataset. This bulk-first, diff-later approach drastically decreases the total number of round-trip requests to the backend. In scenarios where hundreds of simultaneous users each require continuous data access, an offline-ready client that only periodically sends or receives changes can scale more efficiently, freeing your servers to handle more users or other tasks. Instead of being bombarded with frequent small queries and updates, the server focuses on periodic sync operations, which can be more easily optimized or batched. It ",(0,i.jsx)(s.strong,{children:"Scales with Data, Not Load"}),". In fact for most type of apps, most of the data itself rarely changes. Imagine a CRM system, how often does the data of a customer really change compared to how of a user open the customer-overview page which would load data from a server in traditional systems?"]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Less Need for Custom API Endpoints"}),": A local-first architecture often simplifies backend design. Instead of writing extensive REST routes for each client operation (create, read, update, delete, etc.), you can build a ",(0,i.jsx)(s.strong,{children:"single replication endpoint"})," or a small set of endpoints to handle data synchronization for each entity. The client manages local data, merges edits, and pushes/pulls changes with the server automatically. This not only ",(0,i.jsx)(s.strong,{children:"reduces boilerplate code"})," on the backend but also ",(0,i.jsx)(s.strong,{children:"frees developers"})," to focus on business logic and domain-specific concerns rather than spending time creating and maintaining dozens of narrowly scoped endpoints. As a result, the overall system can be easier to scale and maintain, delivering a ",(0,i.jsx)(s.strong,{children:"smoother developer experience"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Simplified State Management in Frontend"}),': Because the local database holds the authoritative state, you might rely less on complex state management libraries (Redux, MobX, etc.). The DB becomes a single source of truth for your UI. In an offline-first app, your global state is already there in a single place stored inside of the local database, so you don\'t need as many in-memory state layers to synchronize\u200b. The UI can directly bind to the database (using queries or reactive subscriptions). All sources of changes (user input, remote updates, other tabs) funnel through the database. This can significantly reduce the "glue code" to keep UI state in sync with server state because the local DB does that for you. With Local-First tools, "you might not need Redux" because the reactive DB fulfills that role\u200b of state management already.']}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Observable Queries"}),": One of the big advantages of storing data locally is the ability to ",(0,i.jsx)(s.strong,{children:"subscribe"})," to data changes in real time, often called ",(0,i.jsx)(s.strong,{children:"observable queries"}),". When the data changes - either from the user's actions or from a remote sync - the local database automatically updates the subscribed query results, and the UI can redraw without a manual refresh. This reactive pattern can make apps feel much more live and responsive."]}),"\n",(0,i.jsxs)(s.p,{children:["In the beginnings this was mostly done by watching a changes feed (like in PouchDB) and ",(0,i.jsx)(s.strong,{children:"re-running queries"})," whenever data changed. However, this early approach was ",(0,i.jsx)(s.strong,{children:"slow and didn't scale well"}),", because the entire query had to be recalculated each time. Later, RxDB introduced the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce Algorithm"}),", which merges incoming document changes into an existing query result by using a big ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/binary-decision-diagram",children:"binary decision tree"}),'. With this, updated query results can be "calculated" on the CPU instead of re-running them over the database. This makes query updates almost instantaneous and scales better as your data grows. Nowadays, many local databases have added similar features: for example, ',(0,i.jsx)(s.strong,{children:"dexie.js"})," introduced ",(0,i.jsx)(s.code,{children:"liveQuery"}),", letting developers build real-time UIs without repeatedly scanning the entire dataset."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Better Multi-Tab and Multi-Device Consistency"}),": Because the source of truth is on the client, if the user has the app open in multiple tabs or even multiple devices, each has a full copy of the data. In browsers, many offline databases use a storage like IndexedDB that is shared across tabs of the same origin. This means all tabs see the same up-to-date local state. For example, if a user logs in or adds data in one tab, the other tab can automatically reflect that change via the shared local DB and events\u200b. This solves a common issue in web apps where one tab doesn't know that something changed in another tab. With local-first, ",(0,i.jsx)(s.strong,{children:"multi-tab just works"}),' by default because there\'s "exactly one state of the data across all tabs". Similarly, on multiple devices, once sync runs, each device eventually converges to the same state.']}),"\n",(0,i.jsxs)(s.blockquote,{children:["\n",(0,i.jsx)(s.p,{children:"If your users have to press F5 all the time, your app is broken!"}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Potential for P2P and Decentralization"}),": While most current local-first apps still use a central backend for syncing, the paradigm opens the door to ",(0,i.jsx)(s.a,{href:"/replication-webrtc.html",children:"peer-to-peer data syncing"}),". Because each device has the full data, devices could sync directly via LAN or other P2P channels for collaboration, reducing reliance on central servers. There are experimental frameworks that allow truly decentralized sync. This is a more advanced benefit, but it aligns with the ethos of giving users more control and reducing dependence on any one cloud provider."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"These advantages show why developers are excited about local-first. You get happier users thanks to a fast, offline-capable app, and you can differentiate your product by working in scenarios where others fail (e.g. poor connectivity). Companies like Google, Amazon, etc., invest heavily in reducing latency and adding offline modes for a reason: it improves retention and usability. Local-first design takes that to the extreme by default."}),"\n",(0,i.jsx)(s.h2,{id:"challenges-and-limitations-of-local-first",children:"Challenges and Limitations of Local-First"}),"\n",(0,i.jsx)(s.p,{children:"However, this approach is not without significant challenges. It's important to understand the drawbacks and trade-offs before deciding to go all-in on local-first. Let's examine the flip side."}),"\n",(0,i.jsxs)(r.G,{author:"Daniel",year:"2024",sourceLink:"https://github.com/pubkey",children:["You fully understood a technology when you know when ",(0,i.jsx)("b",{children:"not"})," to use it"]}),"\n",(0,i.jsx)(s.p,{children:"Critics of local-first approaches often point out these challenges. Here's a comprehensive list of cons, criticisms, and obstacles associated with local-first development and proposed solutions on how to solve these obstacles:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Data Synchronization"}),": Data synchronization is arguably the hardest part of local-first development, because when every user's device can be offline for extended periods, data inevitably diverges. Ensuring those changes propagate and reconcile with minimal user headaches is a major challenge in distributed systems. Two main approaches have emerged:","\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Use a bundled frontend+backend solution"})," where the backend is tightly coupled to the client SDK and knows exactly how to handle sync. A common example is Firestore (part of the Firebase ecosystem) where Google's servers and client libraries collectively manage storage, change detection, conflict resolution, and syncing. The upside is you have a turnkey solution, developers can focus on features rather than writing sync logic. The downside is lock-in, because the sync protocol is proprietary and tailored to that vendor. In many organizations, this is a non-starter: existing company infrastructure can't be uprooted or replaced just for a single offline-capable app. This lock-in issue can arise even if the backend is not strictly a third-party vendor but simply another technology like PostgreSQL, because it still forces you to consolidate all your data into a single system that you might not use otherwise."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Custom Replication with Your Own Endpoints"}),": Alternatively, tools like RxDB allow you to implement your own replication endpoints on top of your existing infrastructure. This is the approach relies on a lightweight, git-like ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Sync Engine"}),'. The server remains relatively "dumb," focusing on storing revisions, tracking changes, and marking conflicts, while the client library does the actual ',(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"conflict resolution"}),". During sync, if the server detects a conflict (e.g., two offline edits to the same document), it notifies the client, which then decides how to merge them, whether via last-write-wins, a custom merge function, or a ",(0,i.jsx)(s.a,{href:"/crdt.html",children:"CRDT"}),". Setting up custom endpoints does require more development effort, but you avoid vendor lock-in and can integrate seamlessly with your existing database(s). Your system simply needs to support incrementally fetching changes (pull) and accepting local modifications (push), which can be layered on top of nearly any data store or architecture."]}),"\n",(0,i.jsx)(s.p,{children:'Tools which support "any backend" are of course harder to monetize because they cannot sell SaaS services or a Cloud Subscription which is why most tools use a fixed backend instead of an open Sync Engine.'}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)("div",{style:{textAlign:"justify"},children:[(0,i.jsx)("img",{src:"/files/document-replication-conflict.svg",alt:"Conflict Handling",width:"170",className:"img-in-text-right"}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Conflict Resolution"}),": When multiple offline edits happen on the same data, you inevitably get ",(0,i.jsx)(s.strong,{children:"merge conflicts"}),". For example, if two users (or the same user on two devices) both edit the same document offline, when both sync, whose changes win? Local-first systems need a conflict resolution strategy. Some systems use ",(0,i.jsx)(s.strong,{children:"last-write-wins"}),' (firestore) or deterministic revision hashing to pick a "winner" (as in CouchDB/PouchDB)\u200b. This is simple but may drop one user\'s changes. Other approaches keep both versions and merge them either via an implement ',(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html#custom-conflict-handler",children:'"merge-function"'})," or require a ",(0,i.jsx)(s.strong,{children:"manual merge"})," step (e.g., like git conflicts or showing the user a diff UI). More advanced solutions involve ",(0,i.jsx)(s.strong,{children:"CRDTs (Conflict-free Replicated Data Types)"}),' which mathematically merge changes (used for rich text collaboration, for instance). Libraries like Automerge or Yjs implement CRDTs to "magically solve conflicts". But in practice, using CRDTs is also complex and has its own trade-offs and sometimes not even possible like when you need additional data from another instance for a "correct" merge. No matter which route, handling conflicts adds complexity to your app logic or infrastructure. In cloud-based (online-first) apps, you avoid this because everyone is always editing the single up-to-date copy on the server. Local-first shifts that burden to the client side.']}),"\n",(0,i.jsx)(s.p,{children:"Here is an example on how a client-side merge functions works in RxDB:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { deepEqual } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/utils'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myConflictHandler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * isEqual() is used to detect if two documents are"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * equal. This is used internally to detect conflicts."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" isEqual"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(a"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" b) {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * isEqual() is used to detect conflicts or to detect if a"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * document has to be pushed to the remote."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * If the documents are deep equal,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * we have no conflict."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Because deepEqual is CPU expensive,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * on your custom conflict handler you might only"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * check some properties, like the updatedAt time or revision-strings"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * for better performance."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" deepEqual"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(a"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" b);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * resolve() a conflict. This can be async so"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * you could even show an UI element to let your user"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * resolve the conflict manually."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" resolve"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(i) {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * In this example we drop the local state and use the server-state."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:' * This basically implements a "first-on-server-wins" strategy.'})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * In your custom conflict handler you could want to merge properties"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * of the i.realMasterState, i.assumedMasterState and i.newDocumentState"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:' * or return i.newDocumentState to have a "last-write-wins" strategy.'})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" i"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".realMasterState;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n"]}),"\n"]})]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Eventual Consistency (No Single Source of Truth):"})," A local-first system is ",(0,i.jsx)(s.strong,{children:"eventually consistent"})," by nature. There is no single authoritative copy of the data at all times. Instead you have one per device (and maybe one on the server), and they sync to become consistent eventually. This means at any given moment, two users might not see the same data if one hasn't synced recently. Users could even make decisions based on stale data. In many applications this is acceptable (the data will catch up), but for some scenarios it's problematic. For instance, an offline-first banking app that lets you initiate a money transfer offline could be dangerous if the account balance was out-of-date or if the transfer needs immediate consistency. Essentially, ",(0,i.jsx)(s.strong,{children:"not all apps can tolerate eventual consistency"}),". If your use case demands strong consistency (e.g., inventory systems where overselling is a big issue, or real-time collaborative editing where every keystroke must be seen by others instantly), a purely local-first approach might need augmentation or may not fit."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Initial Data Load and Data Size Limits:"})," Local-first requires pulling data ",(0,i.jsx)(s.strong,{children:"down to the client"}),". If your dataset is huge (gigabytes), it's simply not feasible to download everything to every client. For example, syncing every tweet on Twitter to every user's phone is impossible. Local-first works best when the data set per user is reasonably sized (up to 2 Gigabytes). In practice, you often ",(0,i.jsx)(s.strong,{children:"limit the data"})," to just that user's own data or a subset relevant to them. Even then, on first use the app might need to download a significant chunk of data to initialize the local database. There is a ",(0,i.jsx)(s.strong,{children:"upper bound on dataset size"})," beyond which the initial sync or storage needs become impractical. You cannot assume unlimited local storage. If your data is too large, local-first will either fail or you'll need to only sync partial data (and then handle what happens if the needed data isn't present locally). In short, ",(0,i.jsx)(s.strong,{children:"local-first is unsuitable for massive datasets"})," or data that cannot be partitioned per user."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)("div",{style:{textAlign:"justify"},children:[(0,i.jsx)("img",{src:"/files/safari-database.png",alt:"safari database",width:"160",className:"img-in-text-right"}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Storage Persistence (Browser Limitations):"})," Storing data in the browser (via IndexedDB or similar) is not as durable as on a server disk. Browsers may ",(0,i.jsx)(s.strong,{children:"evict data"}),' to save space (especially on mobile devices). For instance, Safari notoriously wipes out IndexedDB data if the user hasn\'t used the site in ~7 days. Other browsers have their own eviction policies for offline data. Also, users can at any time clear their browser storage (intentionally or via something like "Clear site data"). This means the local data ',(0,i.jsx)(s.strong,{children:"cannot be 100% trusted to stay forever"}),". A well-behaved local-first app needs to be able to recover if local data is lost, usually by pulling from the server again\u200b. Essentially, the server still often serves as a backup. But if your app had any purely local data (not intended to sync), that's at risk. ",(0,i.jsx)(s.strong,{children:"Mobile apps"})," (with SQLite or filesystem storage) are a bit more stable than web browsers, but even there, uninstalls or certain OS actions can remove local data. This is a challenge: How to cache data offline for speed while ensuring if it's wiped, the user doesn't lose everything important. Cloud-only apps by contrast keep data in the cloud so it's typically safe unless the server fails (and servers are easier to backup reliably)."]}),"\n"]})]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Complex Client-Side Logic & Increased App Size"}),": A local-first app tends to be more complex on the client side. You're essentially putting what used to be server responsibilities (storage, query engine, sync logic) into the frontend. This can increase the size of your frontend bundle (including a database library, possibly CRDT or sync code, etc.). It also increases memory and CPU usage on the client, as the browser/phone is doing more work. Low-end devices or older phones might struggle if the app is not optimized. Developers need to consider performance tuning for the local database (indexing, query efficiency) just like they would on a server. So while the user gains benefits, the app developer has to manage this complexity."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Performance Constraints in JavaScript:"})," Even though devices are fast, a local JS database is generally ",(0,i.jsx)(s.strong,{children:"slower than a server DB"})," on robust hardware. There are many layers (JS -> IndexedDB -> possibly SQLite under the hood) that add overhead\u200b. For example, inserting a record might go through the DB library, the storage engine, the browser's implementation, down to disk. For most UI uses this is fine (you don't need 10k writes/sec in a to-do app, you need maybe a few writes per second at most). But if your app does heavy data processing, the browser might become a bottleneck."]}),"\n",(0,i.jsxs)(s.p,{children:["The key question is ",(0,i.jsx)(s.em,{children:'"Is it fast enough?"'}),". Often the answer is yes for typical usage, but developers must be mindful of not doing something on the client that truly requires big iron servers. For instance, full-text indexing of a million documents might be too slow in a client-side DB. ",(0,i.jsx)(s.strong,{children:"Unpredictable performance"})," is also a factor: Different users have different devices. A query that takes 50ms on a high-end desktop might take 500ms on a low-end phone in battery saving mode. So performance tuning and testing across devices is needed, and some heavy tasks might still belong on the server side\u200b. For example if you build a ",(0,i.jsx)(s.a,{href:"/articles/javascript-vector-database.html",children:"local vector database"})," you might want to create the embeddings on the server and sync them instead of creating them on the client."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Client Database Migrations:"})," As your app evolves, you'll change data models or add new fields. In a cloud-first app, you'd typically run a migration on the server database. In a local-first app, you have not only the server DB (if any) but also every client's local database to consider. Upgrading the schema means you need to write migration logic that runs on each client, perhaps the next time they launch the app after an update. Clients may be offline or not upgrade the app immediately, so you could have different versions of the schema in the wild. This complicates data handling (the sync protocol might need to handle multiple schema versions until everyone is updated). Providing a smooth migration path for local data is doable (many libraries provide ",(0,i.jsx)(s.a,{href:"/migration-schema.html",children:"migration facilities"}),"), but it requires careful testing. In a worst case, a failed migration on a client could brick the app for that user or force a full resync. This is a ",(0,i.jsx)(s.strong,{children:"much bigger headache"})," than just migrating a centralized DB at midnight while your service is in maintenance mode. \ud83c\udf03"]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Security and Access Control:"})," In cloud-based apps, enforcing data security (who can see what) is done on the server. The client only gets the data it's authorized to get. In a local-first scenario, you often need to ",(0,i.jsx)(s.strong,{children:"partition data per user"})," on the backend as well, to ensure users only sync down their own data (or data they have permission for). One simple strategy is to give each user their own database or dataset on the server and only replicate that. For example, CouchDB allows creating one database per user and replication can be scoped to that DB which makes permission handling easy. But if you ever need to query across users (say an admin view or aggregate analytics), having data split into many small DBs becomes a pain. The alternative is a single backend database with a ",(0,i.jsx)(s.strong,{children:"fine-grained access control"}),", and the client asks to sync only certain documents/fields. That usually means writing a custom sync server or using something like GraphQL with resolvers that respect permissions. In short, ",(0,i.jsx)(s.strong,{children:"implementing auth and permissions in sync"})," adds complexity. Also, any data stored on the client is theoretically vulnerable to extraction (if someone compromises the device or uses dev tools). You can ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"encrypt local databases"}),' to prevent extraction after the server "revokes" the decryption password to mitigate the data extraction risk.']}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)("div",{style:{textAlign:"justify"},children:[(0,i.jsx)("img",{src:"/files/no-sql.png",alt:"NoSQL Document",width:"100",className:"img-in-text-right"}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Relational Data and Complex Queries:"})," Most client-side/offline databases are ",(0,i.jsx)(s.a,{href:"/why-nosql.html",children:"NoSQL/document oriented"})," for flexibility in syncing and easy conflict handling. They may not support complex join queries or ACID transactions across multiple tables like a full SQL database would. This is partly because replicating a full relational model is much harder (maintaining referential integrity, etc., when data is partial on a client) or not even logically possible. For example if you have two offline clients running a complex ",(0,i.jsx)(s.code,{children:"UPDATE X WHERE Y FROM Z INNER JOIN Alice INNER JOIN Bob"})," query and then they go online, you have no easy way of handling these conflicts."]}),"\n",(0,i.jsxs)(s.p,{children:["If your app has heavy relational data requirements or relies on complex server-side queries (aggregations, multi-join reports), you might find the local database either cannot do it or is too slow to do it client-side. The lack of robust relational querying is something to plan for and you might need to adjust your data model to be more document-oriented or use client-side libraries to run ",(0,i.jsx)(s.a,{href:"/why-nosql.html#relational-queries-in-nosql",children:"joins in memory"}),". Most tools use NoSQL because it makes replication easy and implementing true relational sync would require extremely sophisticated solutions and even needing an atomic clock for full consistency across nodes (like google spanner). So, ",(0,i.jsx)(s.strong,{children:"if your app truly needs SQL power on the client"}),", local-first might complicate things."]}),"\n",(0,i.jsxs)(s.blockquote,{children:["\n",(0,i.jsx)(s.p,{children:"In Local-First, most tools use NoSQL because it makes replication and conflict handling easy."}),"\n"]}),"\n"]}),"\n"]})]}),"\n",(0,i.jsxs)(s.p,{children:["That's a long list of challenges! In summary, local-first approaches introduce distributed data issues on the client side that web developers usually didn't have to deal with. Despite these challenges, the local-first movement is steadily growing because the ",(0,i.jsx)(s.strong,{children:"benefits to user experience and data control are very compelling"})," and modern tools are emerging to mitigate a lot of these difficulties. All of these are solved or solvable for your specific use-case, just keep them in mind before you start architecting your local-first app."]}),"\n",(0,i.jsx)(s.h2,{id:"local-first-vs-traditional-online-first-approaches",children:"Local-First vs. Traditional Online-First Approaches"}),"\n",(0,i.jsx)(s.p,{children:'So now that you know the pros and cons about Local-First. Lets directly compare it to your previous "online-first" stack:'}),"\n",(0,i.jsx)(s.h3,{id:"connectivity-and-offline-usage",children:"Connectivity and Offline Usage"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Local-first"}),": Apps like WhatsApp store messages locally on your device, letting you read past messages and even compose new ones without a stable network connection. Once online, the messages sync with the server and other participants."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Online-first"}),": Purely cloud-based apps typically stop functioning (beyond simple caching) when the network or server goes down. They rely on a constant connection to fetch or store user data."]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"latency-and-performance",children:"Latency and Performance"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Local-first"}),": Because data operations happen on the device, you see near-instant interactions (e.g., typing and reading chats feels very responsive, as the messages are on your phone). Syncing occurs in the background without delaying the user."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Online-first"}),": Most interactions involve a round-trip to the server, adding network latency and potentially requiring loading states or fallback UIs. If the network is slow or unreliable, users can experience delays when sending or receiving updates."]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"complexity-and-conflict-resolution",children:"Complexity and Conflict Resolution"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Local-first"}),": Much of the complexity like storing data or handling conflicts, shifts to the client. WhatsApp, for instance, caches all messages locally and queues unsent messages offline. Once reconnected, it must reconcile with the server and other devices, especially if the same account is used on multiple platforms."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Online-first"}),": A single central server manages data and concurrency, so clients remain thinner. However, the app becomes unusable if connectivity is lost, and any server downtime directly impacts all users."]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"data-ownership-and-storage-limits",children:"Data Ownership and Storage Limits"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Local-first"}),": Users hold a complete copy of data on their own device, retaining control and quick access. This can raise challenges when data sets are very large; phone storage might be insufficient, or backups may be harder to guarantee."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Online-first"}),": Storing all data in the cloud scales more easily, and providers often manage backups automatically. On the downside, users depend on the service and must trust it to protect their data."]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"when-to-choose-which",children:"When to Choose Which"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Local-first"}),": Particularly helpful for scenarios where offline operation and immediate responsiveness matter, such as chat apps (like WhatsApp), field tools in low-connectivity environments, or any use case where users can't rely on being online 24/7."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Online-first"}),": Well-suited for systems that demand real-time central control or massive data aggregation with minimal offline needs, such as large-scale analytics platforms or services where guaranteed immediate global consistency is essential."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Hybrid"}),": In reality, most modern apps often blend these approaches, giving users partial offline capabilities (caching, queued updates) alongside a robust central service. This hybrid method offers the benefits of local speed and resilience, while still leveraging cloud infrastructure for collaboration and global reach. But a truth local-first app is way more than just a cache."]}),"\n"]}),"\n",(0,i.jsx)(s.hr,{}),"\n",(0,i.jsx)(s.h2,{id:"offline-first-vs-local-first",children:"Offline-First vs. Local-First"}),"\n",(0,i.jsxs)(s.p,{children:["In the early days of offline-capable web apps (around 2014), the common phrase was ",(0,i.jsx)(s.strong,{children:'"Offline-First"'}),". Tools like ",(0,i.jsx)(s.strong,{children:"PouchDB"})," popularized the notion that developers should assume devices are often offline or have flaky connections, so apps must continue to work seamlessly without a network. The guiding principle was ",(0,i.jsx)(s.em,{children:'"apps should treat being online as optional."'})," If a user has no internet access, the application's core features still function, saving or queuing data locally, and automatically synchronizing once connectivity is restored."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)(o.N,{videoId:"bWXAZboHZN8",title:"What is Offline First?",duration:"4:18"})}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsxs)(s.p,{children:["Over time, this focus on offline support evolved into the broader concept of ",(0,i.jsx)(s.strong,{children:'"Local-First Software,"'})," (see ",(0,i.jsx)(s.a,{href:"https://martin.kleppmann.com/papers/local-first.pdf",children:"Ink&Switch"}),") emphasizing not just offline operation but also the technical underpinnings of ",(0,i.jsx)(s.strong,{children:"storing data locally"})," in the client application. While offline-first is primarily about resilience to network loss, local-first highlights ownership, privacy, and performance benefits of keeping the primary data on the user's device. Most tools these days extended the original offline-first concepts, adding real-time reactivity, custom sync, and more nuances like conflict resolution or encryption."]}),"\n",(0,i.jsxs)("div",{style:{textAlign:"justify"},children:[(0,i.jsx)("img",{src:"/files/no-map-tag.png",alt:"no map t ag",width:"100",className:"img-in-text-right"}),(0,i.jsxs)(s.p,{children:["However, the term ",(0,i.jsx)(s.strong,{children:'"local-first"'})," can be ",(0,i.jsx)(s.strong,{children:"confusing"}),' to non-technical audiences because many people (especially in the US) associate "local first" with ',(0,i.jsx)(s.em,{children:"community-oriented movements"})," that encourage buying from nearby businesses or supporting local initiatives. To reduce ambiguity, it may be clearer to use ",(0,i.jsx)(s.strong,{children:'"local first software"'})," or ",(0,i.jsx)(s.strong,{children:'"local first development"'})," in your documentation and marketing materials. When creating branding or logos around local-first software, ",(0,i.jsx)(s.strong,{children:'avoid using the "Google Maps Pin"'}),' as a symbol. This icon typically implies geolocation or physical locality further mixing up the notion of "location-based services" with "on-device data storage."']})]}),"\n",(0,i.jsx)(s.h2,{id:"do-people-actually-use-local-first-or-is-it-just-a-trend",children:"Do People Actually Use Local-First or Is It Just a Trend?"}),"\n",(0,i.jsxs)(s.p,{children:["If we look at ",(0,i.jsx)(s.strong,{children:"npm download statistics"}),", we see that ",(0,i.jsx)(s.strong,{children:"PouchDB"})," - one of the oldest libraries for local-first apps - has about ",(0,i.jsx)(s.strong,{children:"53k"})," downloads each week, and ",(0,i.jsx)(s.strong,{children:"RxDB"})," - a newer library - has about ",(0,i.jsx)(s.strong,{children:"22k"})," weekly downloads. Other local-first tools often have even fewer downloads. In comparison, a popular library like ",(0,i.jsx)(s.strong,{children:"react-query"}),", which does not focus on local storage, is downloaded about ",(0,i.jsx)(s.strong,{children:"1.6 million"})," times a week. These numbers show that local-first libraries, while used, are not as common as some of the more traditional tools."]}),"\n",(0,i.jsxs)(s.p,{children:["One reason is that ",(0,i.jsx)(s.strong,{children:"local-first"}),' is still a new idea. Many developers are used to traditional "online-first" approaches, so switching to a local database and then syncing changes later can feel unfamiliar. Developers must learn different patterns, deal with offline synchronization, and handle possible conflicts. That extra work can be a barrier to adoption which might change in the future as tooling improves.']}),"\n",(0,i.jsxs)(s.p,{children:["While most of RxDB is open source, there are also ",(0,i.jsx)(s.strong,{children:"premium plugins"})," that help sustain RxDB as a long-term project. Because people purchase these plugins, we gains insights into how developers are using local-first features:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["About ",(0,i.jsx)(s.strong,{children:"half"})," of these users mainly want ",(0,i.jsx)(s.strong,{children:"offline functionality"})," for cases such as farming equipment, mining, construction, or even a shrimp farm app."]}),"\n",(0,i.jsxs)(s.li,{children:["The ",(0,i.jsx)(s.strong,{children:"other half"})," focus on ",(0,i.jsx)(s.strong,{children:"faster, real-time UIs"})," for to-do or reading apps, a space launch planning tool, and various dashboard apps. This range of use cases highlights both the resilience offline mode can offer and the performance boost that local databases can provide when synced in the background."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"why-local-first-is-the-future",children:"Why Local-First Is the Future"}),"\n",(0,i.jsxs)(s.p,{children:["Early in the history of the web, users ",(0,i.jsx)(s.strong,{children:"expected"})," static pages. If you wanted to see new content, you ",(0,i.jsx)(s.strong,{children:"reloaded"})," the page. That was normal at the time, and nobody found it strange because everything worked that way."]}),"\n",(0,i.jsxs)(s.p,{children:["Then, as more sites added ",(0,i.jsx)(s.strong,{children:"real-time"}),' features - auto-updating feeds, live notifications, single-page apps - suddenly those older "reload-only" sites began to feel ',(0,i.jsx)(s.strong,{children:"slow"})," or ",(0,i.jsx)(s.strong,{children:"outdated"}),". Why wait for a manual refresh when real-time data was possible and readily available?"]}),"\n",(0,i.jsxs)(s.p,{children:["The same pattern is happening with ",(0,i.jsx)(s.strong,{children:"local-first"})," apps. Right now, most sites are still built around network availability. We see loading spinners whenever data is fetched, and we simply wait for the server response. As local-first experiences become ",(0,i.jsx)(s.strong,{children:"commonplace"})," - removing spinners, letting users keep working when offline, and syncing in the background - everything else will start to feel ",(0,i.jsx)(s.strong,{children:"frustratingly behind"}),". Users won't tolerate slow or blocked interactions if they've seen apps that respond instantly and remain usable offline. They'll expect that as the default and we'll likely see growing pressure on developers to eliminate those extra loading steps. For many users, the experience of ",(0,i.jsx)(s.strong,{children:"immediate local writes will become not just a perk, but an expectation!"})]}),"\n",(0,i.jsx)(s.h2,{id:"see-also",children:"See also"}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Angular Database",width:"220"})})}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Discuss ",(0,i.jsx)(s.a,{href:"https://news.ycombinator.com/item?id=43289885",children:"this topic on HackerNews"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/alternatives.html",children:"Local-First Technologies"}),": A list of databases and technologies (besides ",(0,i.jsx)(s.a,{href:"/",children:"RxDB"}),") that support offline-first or local-first use cases."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/chat/",children:"Discord"}),": Join our Discord server to talk with people and share ideas about this topic."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"https://martin.kleppmann.com/papers/local-first.pdf",children:"Inc&Switch"}),': The "original" paper about Local-First from 2019 where the naming of local-first Software was first used and described.']}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/quickstart.html",children:"Learn how to build a local-first Application with RxDB"}),"."]}),"\n"]})]})}function u(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453(e,s,t){t.d(s,{R:()=>r,x:()=>o});var n=t(6540);const i={},a=n.createContext(i);function r(e){const s=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function o(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),n.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/77d975e6.f460ed60.js b/docs/assets/js/77d975e6.f460ed60.js
deleted file mode 100644
index 07a2b725613..00000000000
--- a/docs/assets/js/77d975e6.f460ed60.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3949],{6805(e,a,s){s.r(a),s.d(a,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>i,toc:()=>d});const i=JSON.parse('{"id":"articles/in-memory-nosql-database","title":"RxDB In-Memory NoSQL - Supercharge Real-Time Apps","description":"Discover how RxDB\'s in-memory NoSQL engine delivers blazing speed for real-time apps, ensuring responsive experiences and seamless data sync.","source":"@site/docs/articles/in-memory-nosql-database.md","sourceDirName":"articles","slug":"/articles/in-memory-nosql-database.html","permalink":"/articles/in-memory-nosql-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB In-Memory NoSQL - Supercharge Real-Time Apps","slug":"in-memory-nosql-database.html","description":"Discover how RxDB\'s in-memory NoSQL engine delivers blazing speed for real-time apps, ensuring responsive experiences and seamless data sync."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB - The Ultimate JS Frontend Database","permalink":"/articles/frontend-database.html"},"next":{"title":"RxDB - The Perfect Ionic Database","permalink":"/articles/ionic-database.html"}}');var n=s(4848),r=s(8453);const t={title:"RxDB In-Memory NoSQL - Supercharge Real-Time Apps",slug:"in-memory-nosql-database.html",description:"Discover how RxDB's in-memory NoSQL engine delivers blazing speed for real-time apps, ensuring responsive experiences and seamless data sync."},o="RxDB as In-memory NoSQL Database: Empowering Real-Time Applications",l={},d=[{value:"Speed and Performance Benefits",id:"speed-and-performance-benefits",level:2},{value:"Persistence Options",id:"persistence-options",level:2},{value:"Use Cases for RxDB",id:"use-cases-for-rxdb",level:2}];function c(e){const a={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,r.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(a.header,{children:(0,n.jsx)(a.h1,{id:"rxdb-as-in-memory-nosql-database-empowering-real-time-applications",children:"RxDB as In-memory NoSQL Database: Empowering Real-Time Applications"})}),"\n",(0,n.jsxs)(a.p,{children:["Real-time applications have become increasingly popular in today's digital landscape. From instant messaging to collaborative editing tools, the demand for responsive and interactive software is on the rise. To meet these requirements, developers need powerful and efficient database solutions that can handle large amounts of data in real-time. ",(0,n.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"}),", an javascript NoSQL database, is revolutionizing the way developers build and scale their applications by offering exceptional speed, flexibility, and scalability."]}),"\n",(0,n.jsx)("center",{children:(0,n.jsx)("a",{href:"https://rxdb.info/",children:(0,n.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"RxDB Flutter Database",width:"220"})})}),"\n",(0,n.jsx)(a.h2,{id:"speed-and-performance-benefits",children:"Speed and Performance Benefits"}),"\n",(0,n.jsx)(a.p,{children:"One of the key advantages of using RxDB as an in-memory NoSQL database is its ability to leverage in-memory storage for faster database operations. By storing data directly in memory, database operations can be performed significantly faster compared to traditional disk-based databases. This is especially important for real-time applications where every millisecond counts. With RxDB, developers can achieve near-instantaneous data access and manipulation, enabling highly responsive user experiences."}),"\n",(0,n.jsx)(a.p,{children:"Additionally, RxDB eliminates disk I/O bottlenecks that are typically associated with traditional databases. In traditional databases, disk reads and writes can become a bottleneck as the amount of data grows. In contrast, an in-memory database like RxDB keeps the entire dataset in RAM, eliminating disk access overhead. This makes it an excellent choice for applications dealing with real-time analytics, high-throughput data processing, and caching."}),"\n",(0,n.jsx)(a.h2,{id:"persistence-options",children:"Persistence Options"}),"\n",(0,n.jsxs)(a.p,{children:["While RxDB offers an ",(0,n.jsx)(a.a,{href:"/rx-storage-memory.html",children:"in-memory"})," storage adapter, it also offers ",(0,n.jsx)(a.a,{href:"/rx-storage.html",children:"persistence storages"}),". Adapters such as ",(0,n.jsx)(a.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"}),", ",(0,n.jsx)(a.a,{href:"/rx-storage-sqlite.html",children:"SQLite"}),", and ",(0,n.jsx)(a.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," enable developers to persist data locally in the browser, making applications accessible even when offline. This hybrid approach combines the benefits of in-memory performance with data durability, providing the best of both worlds. Developers can choose the adapter that best suits their needs, balancing the speed of in-memory storage with the long-term data persistence required for certain applications."]}),"\n",(0,n.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,n.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,n.jsxs)(a.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,n.jsxs)(a.span,{"data-line":"",children:[(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,n.jsx)(a.span,{"data-line":"",children:(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,n.jsxs)(a.span,{"data-line":"",children:[(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,n.jsxs)(a.span,{"data-line":"",children:[(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,n.jsx)(a.span,{"data-line":"",children:(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageMemory"})}),"\n",(0,n.jsxs)(a.span,{"data-line":"",children:[(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-memory'"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,n.jsx)(a.span,{"data-line":"",children:" "}),"\n",(0,n.jsxs)(a.span,{"data-line":"",children:[(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,n.jsxs)(a.span,{"data-line":"",children:[(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,n.jsxs)(a.span,{"data-line":"",children:[(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMemory"}),(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,n.jsx)(a.span,{"data-line":"",children:(0,n.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,n.jsxs)(a.p,{children:["Also the ",(0,n.jsx)(a.a,{href:"/rx-storage-memory-mapped.html",children:"memory mapped RxStorage"})," exists as a wrapper around any other RxStorage. The wrapper creates an in-memory storage that is used for query and write operations. This memory instance is replicated with the underlying storage for persistence. The main reason to use this is to improve initial page load and query/write times. This is mostly useful in browser based applications."]}),"\n",(0,n.jsx)(a.h2,{id:"use-cases-for-rxdb",children:"Use Cases for RxDB"}),"\n",(0,n.jsx)(a.p,{children:"RxDB's capabilities make it well-suited for various real-time applications. Some notable use cases include:"}),"\n",(0,n.jsxs)(a.ul,{children:["\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Chat Applications and Real-Time Messaging: RxDB's in-memory performance and real-time synchronization capabilities make it an excellent choice for building chat applications and real-time messaging systems. Developers can ensure that messages are delivered and synchronized across multiple clients in real-time, providing a seamless and responsive chat experience."}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Collaborative Document Editors: RxDB's ability to handle data streams and propagate changes in real-time makes it ideal for collaborative document editing. Multiple users can simultaneously edit a document, and their changes are instantly synchronized, allowing for real-time collaboration and ensuring that everyone has the most up-to-date version of the document."}),"\n"]}),"\n",(0,n.jsxs)(a.li,{children:["\n",(0,n.jsx)(a.p,{children:"Real-Time Analytics Dashboards: RxDB's speed and scalability make it a valuable tool for real-time analytics dashboards. It can handle high volumes of data and perform complex analytics operations in real-time, providing instant insights and visualizations to users."}),"\n"]}),"\n"]}),"\n",(0,n.jsx)(a.p,{children:"In conclusion, RxDB serves as a powerful in-memory NoSQL database that empowers developers to build real-time applications with exceptional speed, flexibility, and scalability. Its ability to leverage in-memory storage, eliminate disk I/O bottlenecks, and provide persistence options make it an attractive choice for a wide range of real-time use cases. Whether it's chat applications, collaborative document editors, or real-time analytics dashboards, RxDB provides the foundation for building responsive and interactive software that meets the demands of today's users."})]})}function h(e={}){const{wrapper:a}={...(0,r.R)(),...e.components};return a?(0,n.jsx)(a,{...e,children:(0,n.jsx)(c,{...e})}):c(e)}},8453(e,a,s){s.d(a,{R:()=>t,x:()=>o});var i=s(6540);const n={},r=i.createContext(n);function t(e){const a=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(a):{...a,...e}},[a,e])}function o(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:t(e.components),i.createElement(r.Provider,{value:a},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/7815dd0c.d77e48f5.js b/docs/assets/js/7815dd0c.d77e48f5.js
deleted file mode 100644
index 1fdc98d9556..00000000000
--- a/docs/assets/js/7815dd0c.d77e48f5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5335],{2015(s,n,e){e.r(n),e.d(n,{assets:()=>t,contentTitle:()=>a,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"population","title":"Populate and Link Docs in RxDB","description":"Learn how to reference and link documents across collections in RxDB. Discover easy population without joins and handle complex relationships.","source":"@site/docs/population.md","sourceDirName":".","slug":"/population.html","permalink":"/population.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Populate and Link Docs in RxDB","slug":"population.html","description":"Learn how to reference and link documents across collections in RxDB. Discover easy population without joins and handle complex relationships."},"sidebar":"tutorialSidebar","previous":{"title":"CRDT","permalink":"/crdt.html"},"next":{"title":"ORM","permalink":"/orm.html"}}');var o=e(4848),i=e(8453);const l={title:"Populate and Link Docs in RxDB",slug:"population.html",description:"Learn how to reference and link documents across collections in RxDB. Discover easy population without joins and handle complex relationships."},a="Population",t={},c=[{value:"Schema with ref",id:"schema-with-ref",level:2},{value:"populate()",id:"populate",level:2},{value:"via method",id:"via-method",level:3},{value:"via getter",id:"via-getter",level:3},{value:"Example with nested reference",id:"example-with-nested-reference",level:2},{value:"Example with array",id:"example-with-array",level:2}];function d(s){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",span:"span",...(0,i.R)(),...s.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"population",children:"Population"})}),"\n",(0,o.jsx)(n.p,{children:"There are no joins in RxDB but sometimes we still want references to documents in other collections. This is where population comes in. You can specify a relation from one RxDocument to another RxDocument in the same or another RxCollection of the same database.\nThen you can get the referenced document with the population-getter."}),"\n",(0,o.jsxs)(n.p,{children:["This works exactly like population with ",(0,o.jsx)(n.a,{href:"http://mongoosejs.com/docs/populate.html",children:"mongoose"}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"schema-with-ref",children:"Schema with ref"}),"\n",(0,o.jsxs)(n.p,{children:["The ",(0,o.jsx)(n.code,{children:"ref"}),"-keyword in properties describes to which collection the field-value belongs to (has a relationship)."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" refHuman"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human related to other human'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" bestFriend"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ref"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // refers to collection human"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // ref-values must always be string or ['string','null'] (primary of foreign RxDocument) "})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,o.jsx)(n.p,{children:"You can also have a one-to-many reference by using a string-array."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" schemaWithOneToManyReference"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" friends"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'array'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ref"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" items"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"populate",children:"populate()"}),"\n",(0,o.jsx)(n.h3,{id:"via-method",children:"via method"}),"\n",(0,o.jsx)(n.p,{children:"To get the referred RxDocument, you can use the populate()-method.\nIt takes the field-path as attribute and returns a Promise which resolves to the foreign document or null if not found."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" humansCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Alice'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" bestFriend"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Carol'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" humansCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bob'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" bestFriend"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Alice'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" humansCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Bob'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" bestFriend"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".populate"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'bestFriend'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(bestFriend); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> RxDocument[Alice]"})]})]})})}),"\n",(0,o.jsx)(n.h3,{id:"via-getter",children:"via getter"}),"\n",(0,o.jsxs)(n.p,{children:["You can also get the populated RxDocument with the direct getter. Therefore you have to add an underscore suffix ",(0,o.jsx)(n.code,{children:"_"})," to the fieldname.\nThis works also on nested values."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" humansCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Alice'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" bestFriend"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Carol'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" humansCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bob'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" bestFriend"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Alice'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" humansCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Bob'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" bestFriend"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".bestFriend_; "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// notice the underscore_"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(bestFriend); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> RxDocument[Alice]"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"example-with-nested-reference",children:"Example with nested reference"}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" human"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" family"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mother"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ref"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mother"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"family"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".mother_;"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(mother); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> RxDocument"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"example-with-array",children:"Example with array"}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" human"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" friends"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'array'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ref"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'human'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" items"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } "})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//[insert other humans here]"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Alice'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" friends"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bob'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Carol'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Dave'"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" humansCollection"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Alice'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" friends"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".friends_;"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(friends); "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> Array."})]})]})})})]})}function h(s={}){const{wrapper:n}={...(0,i.R)(),...s.components};return n?(0,o.jsx)(n,{...s,children:(0,o.jsx)(d,{...s})}):d(s)}},8453(s,n,e){e.d(n,{R:()=>l,x:()=>a});var r=e(6540);const o={},i=r.createContext(o);function l(s){const n=r.useContext(i);return r.useMemo(function(){return"function"==typeof s?s(n):{...n,...s}},[n,s])}function a(s){let n;return n=s.disableParentContext?"function"==typeof s.components?s.components(o):s.components||o:l(s.components),r.createElement(i.Provider,{value:n},s.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/7bbb96fd.75e20d36.js b/docs/assets/js/7bbb96fd.75e20d36.js
deleted file mode 100644
index d8768d5093f..00000000000
--- a/docs/assets/js/7bbb96fd.75e20d36.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3495],{8435(e,s,i){i.r(s),i.d(s,{default:()=>x});var r=i(4586),t=i(8711),l=i(6540),n=i(8141),c=i(4848);function x(){const{siteConfig:e}=(0,r.A)();return(0,l.useEffect)(()=>{(0,n.c)("paid-meeting-link-clicked",100,1)}),(0,c.jsx)(t.A,{title:`Service Request Submitted - ${e.title}`,description:"RxDB Meeting Scheduler",children:(0,c.jsxs)("main",{children:[(0,c.jsx)("br",{}),(0,c.jsx)("br",{}),(0,c.jsx)("br",{}),(0,c.jsx)("br",{}),(0,c.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,c.jsx)("a",{href:"/",target:"_blank",children:(0,c.jsx)("div",{className:"logo",children:(0,c.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:120})})}),(0,c.jsx)("br",{}),(0,c.jsx)("br",{}),(0,c.jsx)("h1",{children:"RxDB Service Form Submitted"}),(0,c.jsx)("br",{}),(0,c.jsxs)("p",{style:{padding:50},children:["Thank you for submitting the form. You will directly get a confirmation email.",(0,c.jsx)("br",{}),(0,c.jsx)("b",{children:"Please check your spam folder!"}),".",(0,c.jsx)("br",{}),"In the next 24 hours you will get a full answer via email."]}),(0,c.jsx)("br",{}),(0,c.jsx)("br",{})]})]})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/7f02c700.bd5b1cc8.js b/docs/assets/js/7f02c700.bd5b1cc8.js
deleted file mode 100644
index 31a00c2df15..00000000000
--- a/docs/assets/js/7f02c700.bd5b1cc8.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9592],{3247(e,s,n){n.d(s,{g:()=>i});var r=n(4848);function i(e){const s=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&s.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&s.push(n),(0,r.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:s+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,r.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5589(e,s,n){n.r(s),n.d(s,{assets:()=>c,contentTitle:()=>a,default:()=>p,frontMatter:()=>l,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"replication-firestore","title":"Smooth Firestore Sync for Offline Apps","description":"Leverage RxDB to enable real-time, offline-first replication with Firestore. Cut cloud costs, resolve conflicts, and speed up your app.","source":"@site/docs/replication-firestore.md","sourceDirName":".","slug":"/replication-firestore.html","permalink":"/replication-firestore.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Smooth Firestore Sync for Offline Apps","slug":"replication-firestore.html","description":"Leverage RxDB to enable real-time, offline-first replication with Firestore. Cut cloud costs, resolve conflicts, and speed up your app."},"sidebar":"tutorialSidebar","previous":{"title":"WebRTC P2P Replication","permalink":"/replication-webrtc.html"},"next":{"title":"MongoDB Replication","permalink":"/replication-mongodb.html"}}');var i=n(4848),o=n(8453),t=n(3247);const l={title:"Smooth Firestore Sync for Offline Apps",slug:"replication-firestore.html",description:"Leverage RxDB to enable real-time, offline-first replication with Firestore. Cut cloud costs, resolve conflicts, and speed up your app."},a="Replication with Firestore from Firebase",c={},d=[{value:"Usage",id:"usage",level:2},{value:"Install the firebase package",id:"install-the-firebase-package",level:3},{value:"Initialize your Firestore Database",id:"initialize-your-firestore-database",level:3},{value:"Start the Replication",id:"start-the-replication",level:3},{value:"Handling deletes",id:"handling-deletes",level:2},{value:"Do not set enableIndexedDbPersistence()",id:"do-not-set-enableindexeddbpersistence",level:2},{value:"Using the replication with an already existing Firestore Database State",id:"using-the-replication-with-an-already-existing-firestore-database-state",level:2},{value:"Filtered Replication",id:"filtered-replication",level:2}];function h(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"replication-with-firestore-from-firebase",children:"Replication with Firestore from Firebase"})}),"\n",(0,i.jsxs)(s.p,{children:["With the ",(0,i.jsx)(s.code,{children:"replication-firestore"})," plugin you can do a two-way realtime replication\nbetween your client side ",(0,i.jsx)(s.a,{href:"./",children:"RxDB"})," Database and a ",(0,i.jsx)(s.a,{href:"https://firebase.google.com/docs/firestore",children:"Cloud Firestore"})," database that is hosted on the Firebase platform. It will use the ",(0,i.jsx)(s.a,{href:"/replication.html",children:"RxDB Sync Engine"})," to manage the replication streams, error- and conflict handling."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/alternatives/firebase.svg",alt:"Firebase",height:"40"})}),"\n",(0,i.jsx)(s.p,{children:"Replicating your Firestore state to RxDB can bring multiple benefits compared to using the Firestore directly:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"It can reduce your cloud fees because your queries run against the local state of the documents without touching a server and writes can be batched up locally and send to the backend in bulks. This is mostly the case for read heavy applications."}),"\n",(0,i.jsxs)(s.li,{children:["You can run complex ",(0,i.jsx)(s.a,{href:"/why-nosql.html",children:"NoSQL queries"})," on your documents because you are not bound to the ",(0,i.jsx)(s.a,{href:"https://firebase.google.com/docs/firestore/query-data/queries",children:"Firestore Query"})," handling. You can also use local indexes, ",(0,i.jsx)(s.a,{href:"/key-compression.html",children:"compression"})," and ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"encryption"})," and do things like fulltext search, fully locally."]}),"\n",(0,i.jsxs)(s.li,{children:["Your application can be truly ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"Offline-First"})," because your data is stored in a client side database. In contrast Firestore by itself only provides options to support ",(0,i.jsx)(s.a,{href:"https://cloud.google.com/firestore/docs/manage-data/enable-offline",children:"offline also"})," which more works like a cache and requires the user to be online at application start to run authentication."]}),"\n",(0,i.jsxs)(s.li,{children:["It reduces the vendor lock in because you can switch out the backend server afterwards without having to rebuild big parts of the application. RxDB supports replication plugins with multiple technologies and it is even easy to set up with your ",(0,i.jsx)(s.a,{href:"/replication.html",children:"custom backend"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["You can use sophisticated ",(0,i.jsx)(s.a,{href:"/replication.html#conflict-handling",children:"conflict resolution strategies"})," so you are not bound to the Firestore ",(0,i.jsx)(s.a,{href:"https://stackoverflow.com/a/47781502/3443137",children:"last-write-wins"})," strategy which is not suitable for many applications."]}),"\n",(0,i.jsx)(s.li,{children:"The initial load time of your application can be decreased because it will do an incremental replication on restarts."}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(t.g,{children:[(0,i.jsx)(s.h3,{id:"install-the-firebase-package",children:"Install the firebase package"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" firebase"})]})})})}),(0,i.jsx)(s.h3,{id:"initialize-your-firestore-database",children:"Initialize your Firestore Database"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" *"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" as"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firebase "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firebase/app'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getFirestore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firebase/firestore'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" projectId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-project-id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" app"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" firebase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".initializeApp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" projectId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" databaseURL"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://localhost:8080?ns='"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" projectId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" firestoreDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getFirestore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(app);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" firestoreCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(firestoreDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-collection-name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),(0,i.jsx)(s.h3,{id:"start-the-replication",children:"Start the Replication"}),(0,i.jsxs)(s.p,{children:["Start the replication by calling ",(0,i.jsx)(s.code,{children:"replicateFirestore()"})," on your ",(0,i.jsx)(s.a,{href:"/rx-collection.html",children:"RxCollection"}),"."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateFirestore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `https://firestore.googleapis.com/"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"projectId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firestore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" projectId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firestoreDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firestoreCollection"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * (required) Enable push and pull replication with firestore by"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * providing an object with optional filter"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * for each type of replication desired."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=disabled]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Either do a live or a one-time replication"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=true]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * (optional) likely you should just use the default."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" *"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * In firestore it is not possible to read out"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * the internally used write timestamp of a document."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Even if we could read it out, it is not indexed which"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * is required for fetch 'changes-since-x'."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * So instead we have to rely on a custom user defined field"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * that contains the server time"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * which is set by firestore via serverTimestamp()"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Notice that the serverTimestampField MUST NOT be"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * part of the collections RxJsonSchema!"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default='serverTimestamp']"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" serverTimestampField"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'serverTimestamp'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsxs)(s.p,{children:["To observe and cancel the replication, you can use any other methods from the ",(0,i.jsx)(s.a,{href:"/replication.html",children:"ReplicationState"})," like ",(0,i.jsx)(s.code,{children:"error$"}),", ",(0,i.jsx)(s.code,{children:"cancel()"})," and ",(0,i.jsx)(s.code,{children:"awaitInitialReplication()"}),"."]})]}),"\n",(0,i.jsx)(s.h2,{id:"handling-deletes",children:"Handling deletes"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB requires you to never ",(0,i.jsx)(s.a,{href:"/replication.html#data-layout-on-the-server",children:"fully delete documents"}),". This is needed to be able to replicate the deletion state of a document to other instances. The firestore replication will set a boolean ",(0,i.jsx)(s.code,{children:"_deleted"})," field to all documents to indicate the deletion state. You can change this by setting a different ",(0,i.jsx)(s.code,{children:"deletedField"})," in the sync options."]}),"\n",(0,i.jsxs)(s.h2,{id:"do-not-set-enableindexeddbpersistence",children:["Do not set ",(0,i.jsx)(s.code,{children:"enableIndexedDbPersistence()"})]}),"\n",(0,i.jsxs)(s.p,{children:["Firestore has the ",(0,i.jsx)(s.code,{children:"enableIndexedDbPersistence()"})," feature which caches document states locally to IndexedDB. This is not needed when you replicate your Firestore with RxDB because RxDB itself will store the data locally already."]}),"\n",(0,i.jsx)(s.h2,{id:"using-the-replication-with-an-already-existing-firestore-database-state",children:"Using the replication with an already existing Firestore Database State"}),"\n",(0,i.jsxs)(s.p,{children:["If you have not used RxDB before and you already have documents inside of your Firestore database, you have\nto manually set the ",(0,i.jsx)(s.code,{children:"_deleted"})," field to ",(0,i.jsx)(s.code,{children:"false"})," and the ",(0,i.jsx)(s.code,{children:"serverTimestamp"})," to all existing documents."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getDocs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" serverTimestamp"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firebase/firestore'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" allDocsResult"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getDocs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(firestoreCollection));"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"allDocsResult"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".forEach"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".update"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" _deleted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" serverTimestamp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" serverTimestamp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Also notice that if you do writes from non-RxDB applications, you have to keep these fields in sync. It is recommended to use the ",(0,i.jsx)(s.a,{href:"https://firebase.google.com/docs/functions/firestore-events",children:"Firestore triggers"})," to ensure that."]}),"\n",(0,i.jsx)(s.h2,{id:"filtered-replication",children:"Filtered Replication"}),"\n",(0,i.jsxs)(s.p,{children:["You might need to replicate only a subset of your collection, either to or from Firestore. You can achieve this using ",(0,i.jsx)(s.code,{children:"push.filter"})," and ",(0,i.jsx)(s.code,{children:"pull.filter"})," options."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateFirestore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firestore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" projectId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firestoreDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" firestoreCollection"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" filter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" where"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'ownerId'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '=='"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" userId)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" filter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (item) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" item"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".syncEnabled "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Keep in mind that you can not use inequality operators ",(0,i.jsx)(s.code,{children:"(<, <=, !=, not-in, >, or >=)"})," in ",(0,i.jsx)(s.code,{children:"pull.filter"})," since that would cause a conflict with ordering by ",(0,i.jsx)(s.code,{children:"serverTimestamp"}),"."]})]})}function p(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,s,n){n.d(s,{R:()=>t,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function t(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:t(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8070e160.c192f1b9.js b/docs/assets/js/8070e160.c192f1b9.js
deleted file mode 100644
index c91510d7532..00000000000
--- a/docs/assets/js/8070e160.c192f1b9.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3822],{3247(e,s,r){r.d(s,{g:()=>i});var n=r(4848);function i(e){const s=[];let r=null;return e.children.forEach(e=>{e.props.id?(r&&s.push(r),r={headline:e,paragraphs:[]}):r&&r.paragraphs.push(e)}),r&&s.push(r),(0,n.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,n.jsxs)("div",{style:o.stepWrapper,children:[(0,n.jsxs)("div",{style:o.stepIndicator,children:[(0,n.jsx)("div",{style:o.stepNumber,children:s+1}),(0,n.jsx)("div",{style:o.verticalLine})]}),(0,n.jsxs)("div",{style:o.stepContent,children:[(0,n.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,n.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},4143(e,s,r){r.r(s),r.d(s,{assets:()=>p,contentTitle:()=>h,default:()=>j,frontMatter:()=>c,metadata:()=>n,toc:()=>k});const n=JSON.parse('{"id":"quickstart","title":"\ud83d\ude80 Quickstart","description":"Learn how to build a realtime app with RxDB. Follow this quickstart for setup, schema creation, data operations, and real-time syncing.","source":"@site/docs/quickstart.md","sourceDirName":".","slug":"/quickstart.html","permalink":"/quickstart.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"\ud83d\ude80 Quickstart","slug":"quickstart.html","description":"Learn how to build a realtime app with RxDB. Follow this quickstart for setup, schema creation, data operations, and real-time syncing."},"sidebar":"tutorialSidebar","previous":{"title":"Overview","permalink":"/overview.html"},"next":{"title":"Installation","permalink":"/install.html"}}');var i=r(4848),o=r(8453),a=r(3247),l=r(8141),t=r(2636),d=r(3103);const c={title:"\ud83d\ude80 Quickstart",slug:"quickstart.html",description:"Learn how to build a realtime app with RxDB. Follow this quickstart for setup, schema creation, data operations, and real-time syncing."},h="RxDB Quickstart",p={},k=[{value:"Installation",id:"installation",level:3},{value:"Pick a Storage",id:"pick-a-storage",level:3},{value:"LocalStorage",id:"localstorage",level:4},{value:"IndexedDB \ud83d\udc51",id:"indexeddb-",level:4},{value:"Dexie.js",id:"dexiejs",level:4},{value:"SQLite",id:"sqlite",level:4},{value:"And more...",id:"and-more",level:4},{value:"Dev-Mode",id:"dev-mode",level:3},{value:"Schema Validation",id:"schema-validation",level:3},{value:"Create a Database",id:"create-a-database",level:3},{value:"Add a Collection",id:"add-a-collection",level:3},{value:"Insert a document",id:"insert-a-document",level:3},{value:"Run a Query",id:"run-a-query",level:3},{value:"Update a Document",id:"update-a-document",level:3},{value:"Delete a document",id:"delete-a-document",level:3},{value:"Observe a Query",id:"observe-a-query",level:3},{value:"Observe a Document value",id:"observe-a-document-value",level:3},{value:"Sync the Client",id:"sync-the-client",level:3},{value:"HTTP",id:"http",level:4},{value:"GraphQL",id:"graphql",level:4},{value:"WebRTC (P2P)",id:"webrtc-p2p",level:4},{value:"CouchDB",id:"couchdb",level:4},{value:"And more...",id:"and-more-1",level:4},{value:"Next steps",id:"next-steps",level:2}];function x(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components},{Details:r}=s;return r||function(e,s){throw new Error("Expected "+(s?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(l.V,{type:"page_quickstart",value:.2,maxPerUser:1,primary:!1}),"\n",(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"rxdb-quickstart",children:"RxDB Quickstart"})}),"\n",(0,i.jsx)(s.p,{children:"Welcome to the RxDB Quickstart. Here we'll learn how to create a simple real-time app with the RxDB database that is able to store and query data persistently in a browser and does realtime updates to the UI on changes."}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"/files/logo/rxdb_javascript_database.svg",alt:"JavaScript Embedded Database",width:"220"})})}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(s.h3,{id:"installation",children:"Installation"}),(0,i.jsx)(s.p,{children:"Install the RxDB library and the RxJS dependency:"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"})]})})})}),(0,i.jsx)(s.h3,{id:"pick-a-storage",children:"Pick a Storage"}),(0,i.jsx)(s.p,{children:"RxDB is able to run in a wide range of JavaScript runtimes like browsers, mobile apps, desktop and servers. Therefore different storage engines exist that ensure the best performance depending on where RxDB is used."}),(0,i.jsxs)(t.t,{children:[(0,i.jsx)(s.h4,{id:"localstorage",children:"LocalStorage"}),(0,i.jsxs)(s.p,{children:["Use this for the simplest browser setup and very small datasets. It has a tiny bundle size and works anywhere ",(0,i.jsx)(s.a,{href:"/articles/localstorage.html",children:"localStorage"})," is available, but is not optimized for large data or heavy writes."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageLocalstorage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),(0,i.jsx)(s.h4,{id:"indexeddb-",children:"IndexedDB \ud83d\udc51"}),(0,i.jsxs)(s.p,{children:["The premium ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB storage"})," is a high-performance, browser-native storage with a smaller bundle and faster startup compared to Dexie-based IndexedDB. Recommended when you have ",(0,i.jsx)(s.a,{href:"/premium/",children:"\ud83d\udc51 premium"})," access and care about performance and bundle size."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),(0,i.jsx)(s.h4,{id:"dexiejs",children:"Dexie.js"}),(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-dexie.html",children:"Dexie.js"})," is a friendly wrapper around IndexedDB and is a great default for browser apps when you don\u2019t use premium. It\u2019s reliable, works well for medium-sized datasets, and is free to use."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageDexie"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-dexie'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),(0,i.jsx)(s.h4,{id:"sqlite",children:"SQLite"}),(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite"})," is ideal for React Native, Capacitor, Electron, Node.js and other hybrid or native environments. It gives you a fast, durable database on disk. Use the \ud83d\udc51 premium storage for production; a trial version exists for quick experimentation."]}),(0,i.jsx)(s.p,{children:(0,i.jsx)(s.strong,{children:"Premium SQLite (Node.js example)"})}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsNode"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Provide the sqliteBasics adapter for your runtime, e.g. Node.js, React Native, etc."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// For example in Node.js you would derive sqliteBasics from a sqlite3-compatible library:"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqlite3 "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'sqlite3'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsNode"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite3)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.p,{children:(0,i.jsx)(s.strong,{children:"SQLite trial storage (Node.js, free)"})}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLiteTrial"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsNodeNative"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { DatabaseSync } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'node:sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLiteTrial"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsNodeNative"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(DatabaseSync)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"and-more",children:"And more..."}),(0,i.jsxs)(s.p,{children:["There are many more storages such as ",(0,i.jsx)(s.a,{href:"/rx-storage-mongodb.html",children:"MongoDB"}),", ",(0,i.jsx)(s.a,{href:"/rx-storage-denokv.html",children:"DenoKV"}),", ",(0,i.jsx)(s.a,{href:"/rx-storage-filesystem-node.html",children:"Filesystem"}),", ",(0,i.jsx)(s.a,{href:"/rx-storage-memory.html",children:"Memory"}),", ",(0,i.jsx)(s.a,{href:"/rx-storage-memory-mapped.html",children:"Memory-Mapped"}),", ",(0,i.jsx)(s.a,{href:"/rx-storage-foundationdb.html",children:"FoundationDB"})," and more. ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"Browse the full list of storages"}),"."]})]}),(0,i.jsxs)(r,{children:[(0,i.jsx)("summary",{children:"Which storage should I use?"}),(0,i.jsxs)("div",{children:[(0,i.jsx)(s.p,{children:"RxDB provides a wide range of storages depending on your JavaScript runtime and performance needs."}),(0,i.jsxs)("ul",{children:[(0,i.jsxs)("li",{children:["In the Browser: Use the ",(0,i.jsx)("a",{href:"/rx-storage-localstorage.html",children:"LocalStorage"})," storage for simple setup and small build size. For bigger datasets, use either the ",(0,i.jsx)("a",{href:"/rx-storage-dexie.html",children:"dexie.js storage"})," (free) or the ",(0,i.jsx)("a",{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"})," if you have ",(0,i.jsx)("a",{href:"/premium/",children:"\ud83d\udc51 premium access"})," which is a bit faster and has a smaller build size."]}),(0,i.jsxs)("li",{children:["In ",(0,i.jsx)("a",{href:"/electron-database.html",children:"Electron"})," and ",(0,i.jsx)("a",{href:"/react-native-database.html",children:"ReactNative"}),": Use the ",(0,i.jsx)("a",{href:"./rx-storage-sqlite.html",children:"SQLite RxStorage"})," if you have ",(0,i.jsx)("a",{href:"/premium/",children:"\ud83d\udc51 premium access"})," or the ",(0,i.jsx)("a",{href:"/rx-storage-sqlite.html",children:"trial-SQLite RxStorage"})," for tryouts."]}),(0,i.jsxs)("li",{children:["In Capacitor: Use the ",(0,i.jsx)("a",{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," if you have ",(0,i.jsx)("a",{href:"/premium/",children:"\ud83d\udc51 premium access"}),", otherwise use the ",(0,i.jsx)("a",{href:"/rx-storage-localstorage.html",children:"localStorage"})," storage."]})]})]})]}),(0,i.jsx)(s.h3,{id:"dev-mode",children:"Dev-Mode"}),(0,i.jsxs)(s.p,{children:["When you use RxDB in development, you should always enable the ",(0,i.jsx)(s.a,{href:"/dev-mode.html",children:"dev-mode plugin"}),", which adds helpful checks and validations, and tells you if you do something wrong."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBDevModePlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/dev-mode'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBDevModePlugin);"})]})]})})}),(0,i.jsx)(s.h3,{id:"schema-validation",children:"Schema Validation"}),(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/schema-validation.html",children:"Schema validation"})," is required when using dev-mode and recommended (but optional) in production. Wrap your storage with the AJV schema validator to ensure all documents match your schema before being saved."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedValidateAjvStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/validate-ajv'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"storage "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedValidateAjvStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ storage });"})]})]})})}),(0,i.jsx)(s.h3,{id:"create-a-database",children:"Create a Database"}),(0,i.jsx)(s.p,{children:"A database is the top\u2011level container in RxDB, responsible for managing collections, coordinating persistence, and providing reactive change streams."}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"add-a-collection",children:"Add a Collection"}),(0,i.jsxs)(s.p,{children:["An RxDatabase contains ",(0,i.jsx)(s.a,{href:"/rx-collection.html",children:"RxCollection"}),"s for storing and querying data. A collection is similar to an SQL table, and individual records are stored in the collection as JSON documents. An ",(0,i.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"})," can have as many collections as you need.\nAdd a collection with a ",(0,i.jsx)(s.a,{href:"/rx-schema.html",children:"schema"})," to the database:"]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // name of the collection"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // we use the JSON-schema standard"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have maxLength"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'boolean'"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" timestamp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" format"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'date-time'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'done'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'timestamp'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"insert-a-document",children:"Insert a document"}),(0,i.jsxs)(s.p,{children:["Now that we have an RxCollection we can store some ",(0,i.jsx)(s.a,{href:"/rx-document.html",children:"documents"})," in it."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'todo1'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Learn RxDB'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" timestamp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Date"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".toISOString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"run-a-query",children:"Run a Query"}),(0,i.jsxs)(s.p,{children:["Execute a ",(0,i.jsx)(s.a,{href:"/rx-query.html",children:"query"})," that returns all found documents once:"]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" foundDocuments"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $eq"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),(0,i.jsx)(s.h3,{id:"update-a-document",children:"Update a Document"}),(0,i.jsxs)(s.p,{children:["In the first found document, set ",(0,i.jsx)(s.code,{children:"done"})," to ",(0,i.jsx)(s.code,{children:"true"}),":"]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" firstDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" foundDocuments["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"];"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" firstDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".patch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"delete-a-document",children:"Delete a document"}),(0,i.jsx)(s.p,{children:"Delete the document so that it can no longer be found in queries:"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" firstDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),(0,i.jsx)(s.h3,{id:"observe-a-query",children:"Observe a Query"}),(0,i.jsxs)(s.p,{children:["Subscribe to data changes so that your UI is always up-to-date with the data stored on disk. RxDB allows you to subscribe to data changes even when the change happens in another part of your application, another browser tab, or during database ",(0,i.jsx)(s.a,{href:"/replication.html",children:"replication/synchronization"}),":"]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" observable"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $eq"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}).$ "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get the observable via RxQuery.$;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"observable"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(notDoneDocs "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Currently have '"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" notDoneDocs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ' things to do'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // -> here you would re-render your app to show the updated document list"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"observe-a-document-value",children:"Observe a Document value"}),(0,i.jsxs)(s.p,{children:["You can also subscribe to the fields of a single RxDocument. Add the ",(0,i.jsx)(s.code,{children:"$"})," sign to the desired field and then subscribe to the returned observable."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"done$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(isDone "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'done: '"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" isDone);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"sync-the-client",children:"Sync the Client"}),(0,i.jsxs)(s.p,{children:["RxDB has multiple ",(0,i.jsx)(s.a,{href:"/replication.html",children:"replication plugins"})," to replicate database state with a server."]}),(0,i.jsxs)(t.t,{children:[(0,i.jsx)(s.h4,{id:"http",children:"HTTP"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicateHTTP"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pullQueryBuilderFromRxSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "rxdb/plugins/replication-http"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"replicateHTTP"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (rows) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"https:/example.com/api/todos/push"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" method"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "POST"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"Content-Type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "application/json"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" body"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(rows)"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((res) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (lastCheckpoint) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "https://example.com/api/todos/pull?"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" URLSearchParams"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(lastCheckpoint)"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((res) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"graphql",children:"GraphQL"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateGraphQL } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-graphql'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"replicateGraphQL"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'https://example.com/graphql'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"webrtc-p2p",children:"WebRTC (P2P)"}),(0,i.jsxs)(s.p,{children:["The easiest way to replicate data between your clients' devices is the ",(0,i.jsx)(s.a,{href:"/replication-webrtc.html",children:"WebRTC replication plugin"})," that replicates data between devices without a centralized server. This makes it easy to try out replication without having to host anything:"]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicateWebRTC"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getConnectionHandlerSimplePeer"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-webrtc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"replicateWebRTC"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" connectionHandlerCreator"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getConnectionHandlerSimplePeer"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({})"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" topic"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ''"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- set any app-specific room id here."})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" secret"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mysecret'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"})})]})})}),(0,i.jsx)(s.h4,{id:"couchdb",children:"CouchDB"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateCouchDB } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-couchdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"replicateCouchDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/todos/'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {}"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"and-more-1",children:"And more..."}),(0,i.jsxs)(s.p,{children:["Explore all ",(0,i.jsx)(s.a,{href:"/replication.html",children:"replication plugins"}),", including advanced conflict handling and custom protocols."]}),(0,i.jsx)(d.dQ,{})]})]}),"\n",(0,i.jsx)(s.h2,{id:"next-steps",children:"Next steps"}),"\n",(0,i.jsx)(s.p,{children:"You are now ready to dive deeper into RxDB."}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Start reading the full documentation ",(0,i.jsx)(s.a,{href:"/install.html",children:"here"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["There is a full implementation of the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb-quickstart",children:"quickstart guide"})," so you can clone that repository and play with the code."]}),"\n",(0,i.jsxs)(s.li,{children:["For frameworks and runtimes like Angular, React Native and others, check out the list of ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"example implementations"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["Also please continue reading the documentation, join the community on our ",(0,i.jsx)(s.a,{href:"/chat/",children:"Discord chat"}),", and star the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb",children:"GitHub repo"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["If you are using RxDB in a production environment and are able to support its continued development, please take a look at the ",(0,i.jsx)(s.a,{href:"/premium/",children:"\ud83d\udc51 Premium package"})," which includes additional plugins and utilities."]}),"\n"]})]})}function j(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(x,{...e})}):x(e)}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>l});var n=r(6540);const i={},o=n.createContext(i);function a(e){const s=n.useContext(o);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),n.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8084fe3b.f4614fb9.js b/docs/assets/js/8084fe3b.f4614fb9.js
deleted file mode 100644
index 3ba18451335..00000000000
--- a/docs/assets/js/8084fe3b.f4614fb9.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9515],{7458(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/vue-indexeddb","title":"IndexedDB Database in Vue Apps - The Power of RxDB","description":"Learn how RxDB simplifies IndexedDB in Vue, offering reactive queries, offline-first capabilities, encryption, compression, and effortless integration.","source":"@site/docs/articles/vue-indexeddb.md","sourceDirName":"articles","slug":"/articles/vue-indexeddb.html","permalink":"/articles/vue-indexeddb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"IndexedDB Database in Vue Apps - The Power of RxDB","slug":"vue-indexeddb.html","description":"Learn how RxDB simplifies IndexedDB in Vue, offering reactive queries, offline-first capabilities, encryption, compression, and effortless integration."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB as a Database in a Vue.js Application","permalink":"/articles/vue-database.html"},"next":{"title":"RxDB as a Database in a jQuery Application","permalink":"/articles/jquery-database.html"}}');var i=n(4848),o=n(8453);const a={title:"IndexedDB Database in Vue Apps - The Power of RxDB",slug:"vue-indexeddb.html",description:"Learn how RxDB simplifies IndexedDB in Vue, offering reactive queries, offline-first capabilities, encryption, compression, and effortless integration."},l="IndexedDB Database in Vue Apps - The Power of RxDB",t={},c=[{value:"What is IndexedDB?",id:"what-is-indexeddb",level:2},{value:"Why Use IndexedDB in Vue",id:"why-use-indexeddb-in-vue",level:2},{value:"Why To Not Use Plain IndexedDB",id:"why-to-not-use-plain-indexeddb",level:2},{value:"Set up RxDB in Vue",id:"set-up-rxdb-in-vue",level:2},{value:"Installing RxDB",id:"installing-rxdb",level:3},{value:"Create a Database and Collections",id:"create-a-database-and-collections",level:3},{value:"CRUD Operations",id:"crud-operations",level:3},{value:"Reactive Queries and Live Updates",id:"reactive-queries-and-live-updates",level:2},{value:"Using RxJS Observables with Vue 3 Composition API",id:"using-rxjs-observables-with-vue-3-composition-api",level:3},{value:"Using Vue Signals",id:"using-vue-signals",level:3},{value:"Vue IndexedDB Example with RxDB",id:"vue-indexeddb-example-with-rxdb",level:2},{value:"Advanced RxDB Features",id:"advanced-rxdb-features",level:2},{value:"Limitations of IndexedDB",id:"limitations-of-indexeddb",level:2},{value:"Alternatives to IndexedDB",id:"alternatives-to-indexeddb",level:2},{value:"Performance Comparison with Other Browser Storages",id:"performance-comparison-with-other-browser-storages",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"indexeddb-database-in-vue-apps---the-power-of-rxdb",children:"IndexedDB Database in Vue Apps - The Power of RxDB"})}),"\n",(0,i.jsxs)(s.p,{children:["Building robust, ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"offline-capable"})," Vue applications often involves leveraging browser storage solutions to manage data. IndexedDB is one such powerful tool, but its raw API can be challenging to work with directly. RxDB abstracts away much of IndexedDB's complexity, providing a more developer-friendly experience. In this article, we'll explore what IndexedDB is, why it's beneficial in Vue applications, the challenges of using plain IndexedDB, and how ",(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," can simplify your development process while adding advanced features."]}),"\n",(0,i.jsx)(s.h2,{id:"what-is-indexeddb",children:"What is IndexedDB?"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API",children:"IndexedDB"})," is a low-level API for storing significant amounts of structured data in the browser. It provides a transactional database system that can store key-value pairs, complex objects, and more. This storage engine is asynchronous and supports advanced data types, making it suitable for offline storage and complex web applications."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("img",{src:"/files/icons/vuejs.svg",alt:"Vue IndexedDB",width:"120"})}),"\n",(0,i.jsx)(s.h2,{id:"why-use-indexeddb-in-vue",children:"Why Use IndexedDB in Vue"}),"\n",(0,i.jsx)(s.p,{children:"When building Vue applications, IndexedDB can play a crucial role in enhancing both performance and user experience. Here are some reasons to consider using IndexedDB:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline-First / Local-First"}),": By storing data locally, your application remains functional even without an internet connection."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Performance"}),": Using local data means ",(0,i.jsx)(s.a,{href:"/articles/zero-latency-local-first.html",children:"zero latency"})," and no loading spinners, as data doesn't need to be fetched over a network."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Easier Implementation"}),": Replicating all data to the client once is often simpler than implementing multiple endpoints for each user interaction."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Scalability"}),": Local data reduces server load because queries run on the client side, decreasing server bandwidth and processing requirements."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"why-to-not-use-plain-indexeddb",children:"Why To Not Use Plain IndexedDB"}),"\n",(0,i.jsx)(s.p,{children:"While IndexedDB itself is powerful, its native API comes with several drawbacks for everyday application developers:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Callback-Based API"}),": IndexedDB was originally designed around callbacks rather than modern Promises, making asynchronous code more cumbersome."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Complexity"}),": IndexedDB is low-level, intended for library developers rather than for app developers who just want to store and query data easily."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Basic Query API"}),": Its rudimentary query capabilities limit how you can efficiently perform complex queries. Libraries like RxDB offer more advanced querying and indexing."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"TypeScript Support"}),": Ensuring good ",(0,i.jsx)(s.a,{href:"/tutorials/typescript.html",children:"TypeScript support"})," with IndexedDB is challenging, especially when trying to maintain schema consistency."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Lack of Observable API"}),": IndexedDB doesn't provide an observable API out of the box, making it hard to automatically update your Vue app in real time. RxDB solves this by enabling you to ",(0,i.jsx)(s.a,{href:"/rx-query.html#observe",children:"observe queries"})," or specific documents."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Cross-Tab Communication"}),": Managing cross-tab updates in plain IndexedDB is difficult. RxDB handles this seamlessly - changes in one tab automatically affect observed data in others."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Missing Advanced Features"}),": Features like ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"encryption"})," or ",(0,i.jsx)(s.a,{href:"/key-compression.html",children:"compression"})," aren't built into IndexedDB, but they are available via RxDB."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Limited Platform Support"}),": IndexedDB is browser-only. RxDB offers ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"swappable storages"})," so you can reuse the same data layer code in mobile or desktop environments."]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"set-up-rxdb-in-vue",children:"Set up RxDB in Vue"}),"\n",(0,i.jsx)(s.p,{children:"Setting up RxDB with Vue is straightforward. It abstracts IndexedDB complexities and adds a layer of powerful features over it."}),"\n",(0,i.jsx)(s.h3,{id:"installing-rxdb",children:"Installing RxDB"}),"\n",(0,i.jsx)(s.p,{children:"First, install RxDB (and RxJS) from npm:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" --save"})]})})})}),"\n",(0,i.jsx)(s.h3,{id:"create-a-database-and-collections",children:"Create a Database and Collections"}),"\n",(0,i.jsx)(s.p,{children:"RxDB provides two main storage options:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["The free ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage-based storage"})]}),"\n",(0,i.jsxs)(s.li,{children:["The premium plain ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB-based storage"}),", offering faster ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance"})]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"Below is an example of setting up a simple RxDB database using the localstorage-based storage in a Vue app:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// db.ts"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // the name of the database"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Define your schema"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'hero schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" description"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Describes a hero in your app'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // add collections"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroSchema"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"crud-operations",children:"CRUD Operations"}),"\n",(0,i.jsx)(s.p,{children:"Once your database is initialized, you can perform all CRUD operations:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '1'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Iron Man'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Genius-level intellect'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// bulk insert"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".bulkInsert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '2'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Thor'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'God of Thunder'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '3'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Hulk'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Superhuman Strength'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]);"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// find and findOne"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" ironMan"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Iron Man'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// update"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Hulk'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".update"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ $set"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { power"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Unlimited Strength'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } });"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// delete"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" thorDoc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Thor'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" thorDoc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"reactive-queries-and-live-updates",children:"Reactive Queries and Live Updates"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB excels in providing reactive data capabilities, ideal for ",(0,i.jsx)(s.a,{href:"/articles/realtime-database.html",children:"real-time applications"}),". Subscribing to queries automatically updates your Vue components when underlying data changes - even across ",(0,i.jsx)(s.a,{href:"/articles/browser-database.html",children:"browser"})," tabs."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/animations/realtime.gif",alt:"realtime ui updates",width:"700"})}),"\n",(0,i.jsx)(s.h3,{id:"using-rxjs-observables-with-vue-3-composition-api",children:"Using RxJS Observables with Vue 3 Composition API"}),"\n",(0,i.jsx)(s.p,{children:"Here's an example of a Vue component that subscribes to live data updates:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"html","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"html","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"div"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"h2"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">Hero List"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"h2"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" v-for"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"hero in heroes"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" :key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"hero.id"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"strong"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">{{ hero.name }}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"strong"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"> - {{ hero.power }}"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"div"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"script"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" setup"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" lang"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"ts"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { ref"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" onMounted } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'vue'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { initDB } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@/db'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" ref"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"[]>([]);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onMounted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // create an observable query"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // subscribe to the query"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((newHeroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"[]) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".value "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" newHeroes;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"script"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]})]})})}),"\n",(0,i.jsxs)(s.p,{children:["This component subscribes to the collection's changes, ",(0,i.jsx)(s.a,{href:"/articles/optimistic-ui.html",children:"updating the UI"})," automatically whenever the underlying data changes in any browser tab."]}),"\n",(0,i.jsx)(s.h3,{id:"using-vue-signals",children:"Using Vue Signals"}),"\n",(0,i.jsxs)(s.p,{children:["If you're exploring Vue's reactivity transforms or signals, RxDB also offers ",(0,i.jsx)(s.a,{href:"/reactivity.html",children:"custom reactivity factories"})," (",(0,i.jsx)(s.a,{href:"/premium/",children:"premium plugins"})," are required). This allows queries to emit data as signals instead of traditional Observables."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroesSignal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$; "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// $$ indicates a reactive result"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsx)(s.p,{children:"With this, in your Vue template or script, you can directly read from heroesSignal()"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"html","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"html","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"div"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"h2"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">Hero List"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"h2"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" \x3c!-- we read heroesSignal.value which is always up to date --\x3e"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" v-for"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"hero in heroesSignal.value"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" :key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"hero.id"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"strong"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">{{ hero.name }}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"strong"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"> - {{ hero.power }}"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"div"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"vue-indexeddb-example-with-rxdb",children:"Vue IndexedDB Example with RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["A comprehensive example of using RxDB within a Vue application can be found in the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/vue",children:"RxDB GitHub repository"}),". This repository contains sample applications, showcasing best practices and demonstrating how to integrate RxDB for various use cases."]}),"\n",(0,i.jsx)(s.h2,{id:"advanced-rxdb-features",children:"Advanced RxDB Features"}),"\n",(0,i.jsx)(s.p,{children:"RxDB offers many advanced features that extend beyond basic data storage:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/replication.html",children:"RxDB Replication"}),": Synchronize local data with remote databases seamlessly."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/migration-schema.html",children:"Data Migration"}),": Handle schema changes gracefully with automatic data migrations."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/encryption.html",children:"Encryption"}),": Secure your data with built-in encryption capabilities."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/key-compression.html",children:"Compression"}),": Optimize storage using key compression."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"limitations-of-indexeddb",children:"Limitations of IndexedDB"}),"\n",(0,i.jsx)(s.p,{children:"While IndexedDB is powerful, it has some inherent limitations:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Performance: IndexedDB can be slow under certain conditions. Read more: ",(0,i.jsx)(s.a,{href:"/slow-indexeddb.html",children:"Slow IndexedDB"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/articles/indexeddb-max-storage-limit.html",children:"Storage Limits"}),": Browsers impose limits on how much data can be stored. See: ",(0,i.jsx)(s.a,{href:"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html#storage-size-limits",children:"Browser storage limits"}),"."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"alternatives-to-indexeddb",children:"Alternatives to IndexedDB"}),"\n",(0,i.jsxs)(s.p,{children:["Depending on your application's requirements, there are ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"alternative storage solutions"})," to consider:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Origin Private File System (OPFS)"}),": A newer API that can offer better performance. RxDB supports OPFS as well. More info: ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"RxDB OPFS Storage"})]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"SQLite"}),": Ideal for hybrid frameworks or Capacitor, offering native performance. Explore: ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"RxDB SQLite Storage"})]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"performance-comparison-with-other-browser-storages",children:"Performance Comparison with Other Browser Storages"}),"\n",(0,i.jsx)(s.p,{children:"Here is a performance overview of the various browser-based storage implementations of RxDB:"}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"700"})}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Learn how to use RxDB with the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"})," for a guided introduction."]}),"\n",(0,i.jsxs)(s.li,{children:["Check out the ",(0,i.jsx)(s.a,{href:"/code/",children:"RxDB GitHub repository"})," and leave a star \u2b50 if you find it useful."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["By leveraging RxDB on top of IndexedDB, you can create highly responsive, offline-capable Vue applications without dealing with the low-level complexities of IndexedDB directly. With ",(0,i.jsx)(s.a,{href:"/rx-query.html",children:"reactive queries"}),", seamless cross-tab communication, and powerful advanced features, RxDB becomes an invaluable tool in modern web development."]})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/820807a1.82638c55.js b/docs/assets/js/820807a1.82638c55.js
deleted file mode 100644
index 0dd2e6ad232..00000000000
--- a/docs/assets/js/820807a1.82638c55.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7836],{8453(e,i,a){a.d(i,{R:()=>r,x:()=>o});var n=a(6540);const t={},s=n.createContext(t);function r(e){const i=n.useContext(s);return n.useMemo(function(){return"function"==typeof e?e(i):{...i,...e}},[i,e])}function o(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:r(e.components),n.createElement(s.Provider,{value:i},e.children)}},8906(e,i,a){a.r(i),a.d(i,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>r,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"articles/local-database","title":"What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications","description":"An in-depth exploration of local databases and why RxDB excels as a local-first solution for JavaScript applications.","source":"@site/docs/articles/local-database.md","sourceDirName":"articles","slug":"/articles/local-database.html","permalink":"/articles/local-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications","slug":"local-database.html","description":"An in-depth exploration of local databases and why RxDB excels as a local-first solution for JavaScript applications."},"sidebar":"tutorialSidebar","previous":{"title":"Building an Optimistic UI with RxDB","permalink":"/articles/optimistic-ui.html"},"next":{"title":"React Native Encryption and Encrypted Database/Storage","permalink":"/articles/react-native-encryption.html"}}');var t=a(4848),s=a(8453);const r={title:"What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications",slug:"local-database.html",description:"An in-depth exploration of local databases and why RxDB excels as a local-first solution for JavaScript applications."},o="What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications",l={},c=[{value:"Use Cases of Local Databases",id:"use-cases-of-local-databases",level:3},{value:"Performance Optimization",id:"performance-optimization",level:3},{value:"Security and Encryption",id:"security-and-encryption",level:3},{value:"Why RxDB is Optimized for JavaScript Applications",id:"why-rxdb-is-optimized-for-javascript-applications",level:2},{value:"Real-Time Reactivity",id:"real-time-reactivity",level:3},{value:"Offline-First Support",id:"offline-first-support",level:3},{value:"Flexible Data Replication",id:"flexible-data-replication",level:3},{value:"Schema Validation and Versioning",id:"schema-validation-and-versioning",level:3},{value:"Rich Plugin Ecosystem",id:"rich-plugin-ecosystem",level:3},{value:"Multi-Platform Compatibility",id:"multi-platform-compatibility",level:3},{value:"Performance Optimization",id:"performance-optimization-1",level:3},{value:"Proven Reliability",id:"proven-reliability",level:3},{value:"Developer-Friendly Features",id:"developer-friendly-features",level:3},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const i={a:"a",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(i.header,{children:(0,t.jsx)(i.h1,{id:"what-is-a-local-database-and-why-rxdb-is-the-best-local-database-for-javascript-applications",children:"What is a Local Database and Why RxDB is the Best Local Database for JavaScript Applications"})}),"\n",(0,t.jsxs)(i.p,{children:["A ",(0,t.jsx)(i.strong,{children:"local database"})," is a data storage system residing on a user's device, allowing applications to store, query, and manipulate information without needing continuous network access. This approach prioritizes quick data retrieval, efficient updates, and the ability to function in ",(0,t.jsx)(i.a,{href:"/offline-first.html",children:"offline-first"})," scenarios. In contrast, server-based databases require an active internet connection for each request and response cycle, making them more vulnerable to latency, network disruptions, and downtime."]}),"\n",(0,t.jsxs)(i.p,{children:["Local databases often leverage technologies such as ",(0,t.jsx)(i.strong,{children:"IndexedDB"}),", ",(0,t.jsx)(i.strong,{children:"SQLite"}),", or ",(0,t.jsx)(i.strong,{children:"WebSQL"})," (though WebSQL has been deprecated). These technologies manage both structured data (like relational tables) and unstructured data (such as JSON documents). When connectivity is restored, local databases typically sync their changes back to a central server-side database, maintaining consistent and up-to-date records across multiple devices."]}),"\n",(0,t.jsx)(i.h3,{id:"use-cases-of-local-databases",children:"Use Cases of Local Databases"}),"\n",(0,t.jsx)(i.p,{children:"Local databases are particularly beneficial for:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Offline Functionality"}),": Essential for apps that must remain usable without a consistent internet connection, such as note-taking apps or offline-first CRMs. Users can continue adding and editing data, then sync changes once they reconnect."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Low Latency"}),": By reducing round-trips to remote servers, local databases enable real-time responsiveness. This feature is critical for interactive applications such as gaming platforms, data dashboards, or analytics tools that need near-instant feedback."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Data Synchronization"}),": Many modern applications - like chat systems or collaborative editing tools - require continuous data exchange between multiple users or devices. Local databases can handle intermittent connectivity gracefully, queuing updates locally and syncing them when possible."]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["In addition, local databases are increasingly integral to ",(0,t.jsx)(i.strong,{children:"Progressive Web Apps (PWAs)"}),", offering a native app-like user experience that is fast and available, even when offline."]}),"\n",(0,t.jsx)(i.h3,{id:"performance-optimization",children:"Performance Optimization"}),"\n",(0,t.jsx)(i.p,{children:"The primary performance benefit of a local database is its proximity to the application: queries and updates happen directly on the user's device, eliminating the overhead of multiple network hops. Common optimizations include:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Caching"}),": Storing frequently accessed data in memory or on disk to minimize expensive operations."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Batching Writes"}),": Grouping database operations into a single write transaction to reduce overhead and lock contention."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Efficient Indexing"}),": Using appropriate indexes to speed up queries, especially important for applications that handle large data sets or frequent lookups."]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["These techniques ensure that local databases run smoothly, even on lower-powered or ",(0,t.jsx)(i.a,{href:"/articles/mobile-database.html",children:"mobile devices"}),"."]}),"\n",(0,t.jsx)("p",{align:"center",children:(0,t.jsx)("img",{src:"/files/loading-spinner-not-needed.gif",alt:"loading spinner not needed",width:"300"})}),"\n",(0,t.jsx)(i.h3,{id:"security-and-encryption",children:"Security and Encryption"}),"\n",(0,t.jsxs)(i.p,{children:["Storing data on user devices introduces unique security considerations, such as the risk of physical theft or unauthorized access. Consequently, many local databases support ",(0,t.jsx)(i.strong,{children:"encryption"})," options to safeguard sensitive information. Developers can implement additional security measures like ",(0,t.jsx)(i.strong,{children:"device-level encryption"}),", ",(0,t.jsx)(i.strong,{children:"secure storage plugins"}),", and user authentication to further protect data from prying eyes."]}),"\n",(0,t.jsx)(i.hr,{}),"\n",(0,t.jsx)("center",{children:(0,t.jsx)("a",{href:"https://rxdb.info/",children:(0,t.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"RxDB Database",width:"220"})})}),"\n",(0,t.jsx)(i.h2,{id:"why-rxdb-is-optimized-for-javascript-applications",children:"Why RxDB is Optimized for JavaScript Applications"}),"\n",(0,t.jsx)(i.p,{children:"RxDB (Reactive Database) is an offline-first, NoSQL database designed to meet the needs of modern JavaScript applications. Built with a focus on reactivity and real-time data handling, RxDB excels in scenarios where low-latency, offline availability, and scalability are essential."}),"\n",(0,t.jsx)(i.h3,{id:"real-time-reactivity",children:"Real-Time Reactivity"}),"\n",(0,t.jsxs)(i.p,{children:["At the core of RxDB is ",(0,t.jsx)(i.strong,{children:"reactive programming"}),", allowing you to subscribe to changes in your data collections and receive immediate UI updates when records change - no manual polling or refetching required. For instance, a chat application can display incoming messages as soon as they arrive, maintaining a smooth and responsive experience."]}),"\n",(0,t.jsx)("p",{align:"center",children:(0,t.jsx)("img",{src:"/files/multiwindow.gif",alt:"RxDB multi tab",width:"450"})}),"\n",(0,t.jsx)(i.h3,{id:"offline-first-support",children:"Offline-First Support"}),"\n",(0,t.jsxs)(i.p,{children:["RxDB's primary design goal is to work seamlessly in offline environments. Even if your device loses internet connectivity, RxDB enables you to continue reading and writing data. Once the connection is restored, all pending changes are automatically synchronized with your backend. This ",(0,t.jsx)(i.a,{href:"/offline-first.html",children:"offline-first approach"})," is ideal for productivity apps, field service tools, and other scenarios where reliability and user autonomy are paramount."]}),"\n",(0,t.jsx)(i.h3,{id:"flexible-data-replication",children:"Flexible Data Replication"}),"\n",(0,t.jsxs)(i.p,{children:["A standout feature of RxDB is its ",(0,t.jsx)(i.a,{href:"/replication.html",children:"bi-directional replication"}),". It supports synchronization with a variety of backends, such as:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.a,{href:"/replication-couchdb.html",children:"CouchDB"}),": Via the CouchDB replication, facilitating easy integration with any Couch-compatible server."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.a,{href:"/replication-graphql.html",children:"GraphQL Endpoints"}),": Through community plugins, developers can replicate JSON documents to and from GraphQL servers."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.a,{href:"/replication-http.html",children:"Custom Backends"}),": RxDB provides hooks to build custom replication strategies for proprietary or specialized server APIs."]}),"\n"]}),"\n",(0,t.jsx)(i.p,{children:"This flexibility ensures that RxDB fits into diverse architectures without locking you into a single vendor or technology stack."}),"\n",(0,t.jsx)(i.h3,{id:"schema-validation-and-versioning",children:"Schema Validation and Versioning"}),"\n",(0,t.jsxs)(i.p,{children:["Rather than relying on implicit data models, RxDB leverages ",(0,t.jsx)(i.strong,{children:"JSON schema"})," to define document structures. This approach promotes data consistency by enforcing constraints such as required fields and acceptable data formats. As your application grows and changes, RxDB's built-in ",(0,t.jsx)(i.strong,{children:"schema versioning"})," and migration tools help you evolve your database schema safely, minimizing risks of data corruption or loss."]}),"\n",(0,t.jsx)(i.h3,{id:"rich-plugin-ecosystem",children:"Rich Plugin Ecosystem"}),"\n",(0,t.jsxs)(i.p,{children:["One of RxDB's greatest strengths is its ",(0,t.jsx)(i.strong,{children:"pluggable architecture"}),", allowing you to add functionality as needed:"]}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.a,{href:"/encryption.html",children:"Encryption"}),": Secure your data at rest using advanced encryption plugins."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.a,{href:"/fulltext-search.html",children:"Full-Text Search"}),": Integrate powerful text search capabilities for applications that require quick and flexible query options."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.a,{href:"/rx-storage.html",children:"Storage Adapters"}),": Swap out the underlying storage layer (e.g., ",(0,t.jsx)(i.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB in the browser"}),", ",(0,t.jsx)(i.a,{href:"/rx-storage-sqlite.html",children:"SQLite"})," in ",(0,t.jsx)(i.a,{href:"/react-native-database.html",children:"React Native"}),", or a custom engine) without rewriting your application logic."]}),"\n"]}),"\n",(0,t.jsx)(i.p,{children:"You can fine-tune RxDB to your exact needs, avoiding the performance overhead of unnecessary features."}),"\n",(0,t.jsx)(i.h3,{id:"multi-platform-compatibility",children:"Multi-Platform Compatibility"}),"\n",(0,t.jsx)(i.p,{children:"RxDB is a perfect fit for cross-platform development, as it supports numerous environments:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Browsers (IndexedDB)"}),": For web and PWA projects."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Node.js"}),": Ideal for server-side rendering or background services."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"React Native"}),": Leverage SQLite or other adapters for mobile app development."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.a,{href:"/electron-database.html",children:"Electron"}),": Create offline-capable desktop apps with a unified codebase."]}),"\n"]}),"\n",(0,t.jsx)(i.p,{children:"This versatility empowers teams to reuse application logic across multiple platforms while maintaining a consistent data model."}),"\n",(0,t.jsx)(i.h3,{id:"performance-optimization-1",children:"Performance Optimization"}),"\n",(0,t.jsxs)(i.p,{children:["With ",(0,t.jsx)(i.strong,{children:"lazy loading"})," of data and the ability to utilize efficient storage engines, RxDB delivers high-speed operations and quick response times. By minimizing disk I/O and leveraging indexes effectively, RxDB ensures that even large-scale applications remain performant. Its reactive nature also helps avoid unnecessary re-renders, improving the end-user experience."]}),"\n",(0,t.jsx)(i.h3,{id:"proven-reliability",children:"Proven Reliability"}),"\n",(0,t.jsx)(i.p,{children:"RxDB is battle-tested in production environments, handling use cases from small single-user applications to large-scale enterprise solutions. Its robust replication mechanism resolves conflicts, manages concurrent writes, and ensures data integrity. The active open-source community provides ongoing support, documentation updates, and feature improvements."}),"\n",(0,t.jsx)(i.h3,{id:"developer-friendly-features",children:"Developer-Friendly Features"}),"\n",(0,t.jsx)(i.p,{children:"For developers, RxDB offers:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Straightforward APIs"}),": Built on top of familiar JavaScript paradigms like promises and observables."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Excellent Documentation"}),": Detailed guides, tutorials, and references for every major feature."]}),"\n",(0,t.jsxs)(i.li,{children:[(0,t.jsx)(i.strong,{children:"Rich Community Support"}),": Benefit from an active ecosystem of contributors creating plugins, answering questions, and maintaining core libraries."]}),"\n"]}),"\n",(0,t.jsx)(i.p,{children:"These qualities streamline development, making RxDB an appealing choice for teams of all sizes."}),"\n",(0,t.jsx)(i.hr,{}),"\n",(0,t.jsx)(i.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,t.jsx)(i.p,{children:"Ready to get started? Here are some next steps:"}),"\n",(0,t.jsxs)(i.ul,{children:["\n",(0,t.jsxs)(i.li,{children:["Try the ",(0,t.jsx)(i.a,{href:"/quickstart.html",children:"Quickstart Tutorial"})," and build a basic project to see RxDB in action."]}),"\n",(0,t.jsxs)(i.li,{children:["Compare RxDB with ",(0,t.jsx)(i.a,{href:"/alternatives.html",children:"other local database solutions"})," to determine the best fit for your unique requirements."]}),"\n"]}),"\n",(0,t.jsxs)(i.p,{children:["Ultimately, ",(0,t.jsx)(i.strong,{children:"RxDB"})," is more than just a database - it's a robust, reactive toolkit that empowers you to build fast, resilient, and user-centric applications. Whether you're creating an offline-first note-taking app or a real-time collaborative platform, RxDB can handle your local storage needs with ease and flexibility."]})]})}function h(e={}){const{wrapper:i}={...(0,s.R)(),...e.components};return i?(0,t.jsx)(i,{...e,children:(0,t.jsx)(d,{...e})}):d(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8288c265.d455b0a0.js b/docs/assets/js/8288c265.d455b0a0.js
deleted file mode 100644
index 6b3ca9e4cd6..00000000000
--- a/docs/assets/js/8288c265.d455b0a0.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9881],{2309(e,r,s){s.r(r),s.d(r,{assets:()=>l,contentTitle:()=>i,default:()=>c,frontMatter:()=>t,metadata:()=>n,toc:()=>d});const n=JSON.parse('{"id":"releases/11.0.0","title":"RxDB 11 - WebWorker Support & More","description":"RxDB 11.0 brings Worker-based storage for multi-core performance gains. Explore the major changes and migrate seamlessly to harness new speeds.","source":"@site/docs/releases/11.0.0.md","sourceDirName":"releases","slug":"/releases/11.0.0.html","permalink":"/releases/11.0.0.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB 11 - WebWorker Support & More","slug":"11.0.0.html","description":"RxDB 11.0 brings Worker-based storage for multi-core performance gains. Explore the major changes and migrate seamlessly to harness new speeds."},"sidebar":"tutorialSidebar","previous":{"title":"12.0.0","permalink":"/releases/12.0.0.html"},"next":{"title":"10.0.0","permalink":"/releases/10.0.0.html"}}');var o=s(4848),a=s(8453);const t={title:"RxDB 11 - WebWorker Support & More",slug:"11.0.0.html",description:"RxDB 11.0 brings Worker-based storage for multi-core performance gains. Explore the major changes and migrate seamlessly to harness new speeds."},i="11.0.0",l={},d=[{value:"Worker plugin",id:"worker-plugin",level:2},{value:"Transpile async/await to promises instead of generators",id:"transpile-asyncawait-to-promises-instead-of-generators",level:2},{value:"Removed deprecated received methods",id:"removed-deprecated-received-methods",level:2},{value:"All internal events are handled as bulks",id:"all-internal-events-are-handled-as-bulks",level:2},{value:"RxStorage interface changes",id:"rxstorage-interface-changes",level:2},{value:"Removed the no-validate plugin.",id:"removed-the-no-validate-plugin",level:2},{value:"Other changes",id:"other-changes",level:2},{value:"Migration from 10.x.x",id:"migration-from-10xx",level:2},{value:"You can help!",id:"you-can-help",level:2},{value:"Discuss!",id:"discuss",level:2}];function h(e){const r={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(r.header,{children:(0,o.jsx)(r.h1,{id:"1100",children:"11.0.0"})}),"\n",(0,o.jsxs)(r.p,{children:["The last major release was only about ",(0,o.jsx)(r.a,{href:"/releases/10.0.0.html",children:"6 month ago"}),". But to further improve RxDB, it was necessary to make some more breaking changes and release the next major version.\nIn the last version ",(0,o.jsx)(r.code,{children:"10.0.0"})," the storage layer was abstracted in a way to make it possible to not only use PouchDB as storage, but instead we can use different storage engines like the one based on ",(0,o.jsx)(r.a,{href:"/rx-storage-lokijs.html",children:"LokiJS"})," or any custom implementation of the ",(0,o.jsx)(r.code,{children:"RxStorage"})," interface."]}),"\n",(0,o.jsxs)(r.p,{children:["In the new version ",(0,o.jsx)(r.code,{children:"11.0.0"})," the focus is on making it possible to put the RxStorage into a ",(0,o.jsx)(r.strong,{children:"WebWorker"})," to take CPU load from the main process into the worker's process. This can improve the perceived performance of your application, especially when you have to handle many documents or when you need the main process CPU cycles to manage the DOM with your frontend framework."]}),"\n",(0,o.jsx)(r.h2,{id:"worker-plugin",children:"Worker plugin"}),"\n",(0,o.jsxs)(r.p,{children:["Performance was always something RxDB had a struggle with. Not because RxDB itself is slow, but because the underlying storage engine (mostly PouchDB) or ",(0,o.jsx)(r.a,{href:"/slow-indexeddb.html",children:"IndexedDB is slow"}),". This never was a problem for 'normal' applications that have to store some documents. But on big applications with much data, there was a bottleneck."]}),"\n",(0,o.jsxs)(r.p,{children:["With the Worker plugin, you can move the ",(0,o.jsx)(r.code,{children:"RxStorage"})," out of the main JavaScript process. This makes it pretty easy to utilize more than one CPU core and speed up your application."]}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// worker.ts"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedRxStorage } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/worker'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLoki } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-lokijs'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:"wrappedRxStorage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLoki"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// main process"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageWorker } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/worker'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageWorker"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" workerInput"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'path/to/worker.js'"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsxs)(r.p,{children:["The whole documentation about the worker plugin can be found ",(0,o.jsx)(r.a,{href:"/rx-storage-worker.html",children:"here"}),"."]}),"\n",(0,o.jsxs)(r.h2,{id:"transpile-asyncawait-to-promises-instead-of-generators",children:["Transpile ",(0,o.jsx)(r.code,{children:"async"}),"/",(0,o.jsx)(r.code,{children:"await"})," to promises instead of generators"]}),"\n",(0,o.jsxs)(r.p,{children:["The RxDB source-code is transpiled from TypeScript to es5/es6 JavaScript code via babel.\nIn the past we transpiled the ",(0,o.jsx)(r.code,{children:"async"})," and ",(0,o.jsx)(r.code,{children:"await"})," keywords with the babel plugin ",(0,o.jsx)(r.code,{children:"plugin-transform-async-to-generator"}),".\nNow we use the ",(0,o.jsx)(r.a,{href:"https://github.com/rpetrich/babel-plugin-transform-async-to-promises",children:"babel-plugin-transform-async-to-promises"})," plugin instead.\nIt transpiles ",(0,o.jsx)(r.code,{children:"async"}),"/",(0,o.jsx)(r.code,{children:"await"})," into native ",(0,o.jsx)(r.code,{children:"Promise"}),"s instead of using JavaScript generators. This has shown to decrease build size by about 10% and also improves the performance."]}),"\n",(0,o.jsxs)(r.h2,{id:"removed-deprecated-received-methods",children:["Removed deprecated ",(0,o.jsx)(r.code,{children:"received"})," methods"]}),"\n",(0,o.jsxs)(r.p,{children:["In the past there was a typo in all getters and methods that are called ",(0,o.jsx)(r.code,{children:"received"}),".\nThis was renamed to ",(0,o.jsx)(r.code,{children:"received"})," and all mistyped methods have been deprecated.\nWe now removed all deprecated methods, so you have to use the correctly spelled methods instead.\n",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/pull/3392",children:"See #3392"})]}),"\n",(0,o.jsx)(r.h2,{id:"all-internal-events-are-handled-as-bulks",children:"All internal events are handled as bulks"}),"\n",(0,o.jsxs)(r.p,{children:["All events that are generated from writing to the storage instance are now handled in bulks instead of each event for its own. This has shown to save performance when the events are send over a data layer like a ",(0,o.jsx)(r.code,{children:"WebWorker"})," or the ",(0,o.jsx)(r.code,{children:"BroadcastChannel"}),". This change only affects you if you have created custom RxDB plugins."]}),"\n",(0,o.jsx)(r.h2,{id:"rxstorage-interface-changes",children:"RxStorage interface changes"}),"\n",(0,o.jsx)(r.p,{children:"To make the RxStorage abstraction compatible with Webworkers, we had to do some changes. These will only affect you if you use a custom RxStorage implementation."}),"\n",(0,o.jsxs)(r.ul,{children:["\n",(0,o.jsxs)(r.li,{children:["The non async functions ",(0,o.jsx)(r.code,{children:"prepareQuery"}),", ",(0,o.jsx)(r.code,{children:"getSortComparator"})," and ",(0,o.jsx)(r.code,{children:"getQueryMatcher"})," have been moved out of ",(0,o.jsx)(r.code,{children:"RxStorageInstance"})," into the ",(0,o.jsx)(r.code,{children:"statics"})," property of ",(0,o.jsx)(r.code,{children:"RxStorage"}),". This makes it possible to split the code when using the worker plugin. You only need to load the static methods at the main process, and the whole storage engine is only loaded inside of the worker."]}),"\n",(0,o.jsxs)(r.li,{children:["All data communication with the RxStorage now happens only via plain JSON objects. Instead of returning a JavaScript ",(0,o.jsx)(r.code,{children:"Map"})," or ",(0,o.jsx)(r.code,{children:"Set"}),", only ",(0,o.jsx)(r.a,{href:"https://www.w3schools.com/js/js_json_datatypes.asp",children:"JSON datatypes"})," are allowed. This makes it easier to properly serialize the data when transferring it over to or from a WebWorker."]}),"\n",(0,o.jsxs)(r.li,{children:["Events that are created from a write operation, must be emitted ",(0,o.jsx)(r.strong,{children:"before"})," the write operation resolves. This ensures that RxDB always knows about all events before it runs another operation. So when you do an insert and a query directly after the insert, the query will return the correct results."]}),"\n",(0,o.jsxs)(r.li,{children:["The meta data ",(0,o.jsx)(r.code,{children:"digest"})," and ",(0,o.jsx)(r.code,{children:"length"})," of attachments is now created by RxDB, not by the RxStorage. ",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/issues/3548",children:"#3548"})]}),"\n",(0,o.jsxs)(r.li,{children:["Added the statics ",(0,o.jsx)(r.code,{children:"hashKey"})," property to identify the used hash function."]}),"\n"]}),"\n",(0,o.jsxs)(r.h2,{id:"removed-the-no-validate-plugin",children:["Removed the ",(0,o.jsx)(r.code,{children:"no-validate"})," plugin."]}),"\n",(0,o.jsxs)(r.p,{children:["In the past, RxDB required you to add one schema validation plugin. For production, it was useful to not have any schema validation for better performance and a smaller build size. For that, the ",(0,o.jsx)(r.code,{children:"no-validate"})," plugin could be added which was just a dummy plugin that did no do any validation. To remove this unnecessary complexity, RxDB no longer requires you to add a validation plugin. Therefore the no-validate plugin is now removed as it is no longer needed."]}),"\n",(0,o.jsx)(r.h2,{id:"other-changes",children:"Other changes"}),"\n",(0,o.jsxs)(r.ul,{children:["\n",(0,o.jsxs)(r.li,{children:["The LokiJS RxStorage no longer uses the ",(0,o.jsx)(r.code,{children:"IdleQueue"})," to determine if the database is idle. Because LokiJS is in-memory, we can just wait for CPU idleness via ",(0,o.jsx)(r.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback",children:"requestIdleCallback()"})]}),"\n",(0,o.jsx)(r.li,{children:"Bugfix: Do not throw an error when database is destroyed while a GraphQL replication is running."}),"\n",(0,o.jsxs)(r.li,{children:['Compound primary key migration throws "Value of primary key(s) cannot be changed" ',(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/pull/3546",children:"#3546"})]}),"\n",(0,o.jsxs)(r.li,{children:["Allow ",(0,o.jsx)(r.code,{children:"_id"})," as primaryKey ",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/pull/3562",children:"#3562"})," Thanks ",(0,o.jsx)(r.a,{href:"https://github.com/SuperKirik",children:"@SuperKirik"})]}),"\n",(0,o.jsx)(r.li,{children:"LokiJS: Remote operations do never resolve when remote instance was leader and died."}),"\n"]}),"\n",(0,o.jsxs)(r.h2,{id:"migration-from-10xx",children:["Migration from ",(0,o.jsx)(r.code,{children:"10.x.x"})]}),"\n",(0,o.jsx)(r.p,{children:"The migration should be pretty easy. Nothing in the datalayer has been changed, so you can use the stored data of v10 together with the new v11 RxDB."}),"\n",(0,o.jsx)(r.h2,{id:"you-can-help",children:"You can help!"}),"\n",(0,o.jsxs)(r.p,{children:["There are many things that can be done by ",(0,o.jsx)(r.strong,{children:"you"})," to improve RxDB:"]}),"\n",(0,o.jsxs)(r.ul,{children:["\n",(0,o.jsxs)(r.li,{children:["Check the ",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/BACKLOG.md",children:"BACKLOG"})," for features that would be great to have."]}),"\n",(0,o.jsxs)(r.li,{children:["Check the ",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/blob/master/orga/before-next-major.md",children:"breaking backlog"})," for breaking changes that must be implemented in the future but where I did not had the time yet."]}),"\n",(0,o.jsxs)(r.li,{children:["Check the ",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/search?q=todo",children:"todos"})," in the code. There are many small improvements that can be done for performance and build size."]}),"\n",(0,o.jsx)(r.li,{children:"Review the code and add tests. I am only a single human with a laptop. My code is not perfect and much small improvements can be done when people review the code and help me to clarify undefined behaviors."}),"\n",(0,o.jsx)(r.li,{children:"Improve the documentation. In the last user survey many users told me that the documentation is not good enough. But I reviewed the docs and could not find clear flaws. The problem is that I am way to deep into RxDB so that I am not able to understand which documentation a newcomer to the project needs. Likely I assume too much knowledge or focus writing about the wrong parts."}),"\n",(0,o.jsxs)(r.li,{children:["Update the ",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"example projects"})," many of them are outdated and need updates."]}),"\n"]}),"\n",(0,o.jsx)(r.h2,{id:"discuss",children:"Discuss!"}),"\n",(0,o.jsxs)(r.p,{children:["Please ",(0,o.jsx)(r.a,{href:"https://github.com/pubkey/rxdb/issues/3555",children:"discuss here"}),"."]})]})}function c(e={}){const{wrapper:r}={...(0,a.R)(),...e.components};return r?(0,o.jsx)(r,{...e,children:(0,o.jsx)(h,{...e})}):h(e)}},8453(e,r,s){s.d(r,{R:()=>t,x:()=>i});var n=s(6540);const o={},a=n.createContext(o);function t(e){const r=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function i(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:t(e.components),n.createElement(a.Provider,{value:r},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8489a755.3170bb72.js b/docs/assets/js/8489a755.3170bb72.js
deleted file mode 100644
index f6a5c1877ec..00000000000
--- a/docs/assets/js/8489a755.3170bb72.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7700],{7883(e,n,s){s.r(n),s.d(n,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>t,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"articles/jquery-database","title":"RxDB as a Database in a jQuery Application","description":"Level up your jQuery-based projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.","source":"@site/docs/articles/jquery-database.md","sourceDirName":"articles","slug":"/articles/jquery-database.html","permalink":"/articles/jquery-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB as a Database in a jQuery Application","slug":"jquery-database.html","description":"Level up your jQuery-based projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser."},"sidebar":"tutorialSidebar","previous":{"title":"IndexedDB Database in Vue Apps - The Power of RxDB","permalink":"/articles/vue-indexeddb.html"},"next":{"title":"RxDB - Firestore Alternative to Sync with Your Own Backend","permalink":"/articles/firestore-alternative.html"}}');var i=s(4848),a=s(8453),o=s(2271);const t={title:"RxDB as a Database in a jQuery Application",slug:"jquery-database.html",description:"Level up your jQuery-based projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser."},l="RxDB as a Database in a jQuery Application",c={},d=[{value:"jQuery Web Applications",id:"jquery-web-applications",level:2},{value:"Importance of Databases in jQuery Applications",id:"importance-of-databases-in-jquery-applications",level:2},{value:"Introducing RxDB as a Database Solution",id:"introducing-rxdb-as-a-database-solution",level:2},{value:"Key Features",id:"key-features",level:3},{value:"Getting Started with RxDB",id:"getting-started-with-rxdb",level:2},{value:"What is RxDB?",id:"what-is-rxdb",level:3},{value:"Reactive Data Handling",id:"reactive-data-handling",level:3},{value:"Offline-First Approach",id:"offline-first-approach",level:3},{value:"Data Replication",id:"data-replication",level:3},{value:"Observable Queries",id:"observable-queries",level:3},{value:"Multi-Tab Support",id:"multi-tab-support",level:3},{value:"RxDB vs. Other jQuery Database Options",id:"rxdb-vs-other-jquery-database-options",level:3},{value:"Using RxDB in a jQuery Application",id:"using-rxdb-in-a-jquery-application",level:2},{value:"Installing RxDB",id:"installing-rxdb",level:3},{value:"Creating and Configuring a Database",id:"creating-and-configuring-a-database",level:2},{value:"Updating the DOM with jQuery",id:"updating-the-dom-with-jquery",level:2},{value:"Different RxStorage layers for RxDB",id:"different-rxstorage-layers-for-rxdb",level:2},{value:"Synchronizing Data with RxDB between Clients and Servers",id:"synchronizing-data-with-rxdb-between-clients-and-servers",level:2},{value:"Offline-First Approach",id:"offline-first-approach-1",level:3},{value:"Conflict Resolution",id:"conflict-resolution",level:3},{value:"Bidirectional Synchronization",id:"bidirectional-synchronization",level:3},{value:"Advanced RxDB Features and Techniques",id:"advanced-rxdb-features-and-techniques",level:2},{value:"Indexing and Performance Optimization",id:"indexing-and-performance-optimization",level:3},{value:"Encryption of Local Data",id:"encryption-of-local-data",level:3},{value:"Change Streams and Event Handling",id:"change-streams-and-event-handling",level:3},{value:"JSON Key Compression",id:"json-key-compression",level:3},{value:"Best Practices for Using RxDB in jQuery Applications",id:"best-practices-for-using-rxdb-in-jquery-applications",level:2},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"rxdb-as-a-database-in-a-jquery-application",children:"RxDB as a Database in a jQuery Application"})}),"\n",(0,i.jsxs)(n.p,{children:["In the early days of dynamic web development, ",(0,i.jsx)(n.strong,{children:"jQuery"})," emerged as a popular library that simplified DOM manipulation and AJAX requests. Despite the rise of modern frameworks, many developers still maintain or extend existing jQuery projects, or leverage jQuery in specific contexts. As jQuery applications grow in complexity, they often require efficient data handling, offline support, and synchronization capabilities. This is where ",(0,i.jsx)(n.a,{href:"https://rxdb.info/",children:"RxDB"}),", a reactive JavaScript database for the browser, node.js, and ",(0,i.jsx)(n.a,{href:"/articles/mobile-database.html",children:"mobile devices"}),", steps in."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript jQuery Database",width:"220"})})}),"\n",(0,i.jsx)(n.h2,{id:"jquery-web-applications",children:"jQuery Web Applications"}),"\n",(0,i.jsx)(n.p,{children:"jQuery provides a simple API for DOM manipulation, event handling, and AJAX calls. It has been widely adopted due to its ease of use and strong community support. Many projects continue to rely on jQuery for handling client-side functionality, UI interactions, and animations. As these applications evolve, the need for a robust database solution that can manage data locally (and offline) becomes increasingly important."}),"\n",(0,i.jsx)(n.h2,{id:"importance-of-databases-in-jquery-applications",children:"Importance of Databases in jQuery Applications"}),"\n",(0,i.jsx)(n.p,{children:"Modern, data-driven jQuery applications often need to:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Store and retrieve data locally"})," for quick and responsive user experiences."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Synchronize data"})," between clients or with a ",(0,i.jsx)(n.a,{href:"/rx-server.html",children:"central server"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Handle offline scenarios"})," seamlessly."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Handle large or complex data structures"})," without repeatedly hitting the server."]}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["Relying solely on server endpoints or basic browser storage (like ",(0,i.jsx)(n.code,{children:"localStorage"}),") can quickly become unwieldy for larger or more complex use cases. Enter RxDB, a dedicated solution that manages data on the client side while offering real-time synchronization and offline-first capabilities."]}),"\n",(0,i.jsx)(n.h2,{id:"introducing-rxdb-as-a-database-solution",children:"Introducing RxDB as a Database Solution"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB (short for Reactive Database) is built on top of ",(0,i.jsx)(n.a,{href:"/articles/browser-database.html",children:"IndexedDB"})," and leverages ",(0,i.jsx)(n.a,{href:"https://rxjs.dev/",children:"RxJS"})," to provide a modern, reactive approach to handling data in the browser. With RxDB, you can store documents locally, query them in real-time, and synchronize changes with a remote server whenever an internet connection is available."]}),"\n",(0,i.jsx)(n.h3,{id:"key-features",children:"Key Features"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Reactive Data Handling"}),": RxDB emits real-time updates whenever your data changes, allowing you to instantly reflect these changes in the DOM with jQuery."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Offline-First Approach"}),": Keep your application usable even when the user's network is unavailable. Data is automatically synchronized once connectivity is restored."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Data Replication"}),": Enable multi-device or multi-tab synchronization with minimal effort."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Observable Queries"}),": Reduce code complexity by subscribing to queries instead of constantly polling for changes."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Multi-Tab Support"}),": If a user opens your jQuery application in multiple tabs, RxDB keeps data in sync across all sessions."]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)(o.N,{videoId:"qHWrooWyCYg",title:"This solved a problem I've had in Angular for years",duration:"3:45"})}),"\n",(0,i.jsx)(n.h2,{id:"getting-started-with-rxdb",children:"Getting Started with RxDB"}),"\n",(0,i.jsx)(n.h3,{id:"what-is-rxdb",children:"What is RxDB?"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"https://rxdb.info/",children:"RxDB"})," is a client-side NoSQL database that stores data in the browser (or ",(0,i.jsx)(n.a,{href:"/nodejs-database.html",children:"node.js"}),") and synchronizes changes with other instances or servers. Its design embraces reactive programming principles, making it well-suited for real-time applications, offline scenarios, and multi-tab use cases."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/animations/realtime.gif",alt:"real-time ui updates",width:"700"})}),"\n",(0,i.jsx)(n.h3,{id:"reactive-data-handling",children:"Reactive Data Handling"}),"\n",(0,i.jsx)(n.p,{children:"RxDB's use of observables enables an event-driven architecture where data mutations automatically trigger UI updates. In a jQuery application, you can subscribe to these changes and update DOM elements as soon as data changes occur - no need for manual refresh or complicated change detection logic."}),"\n",(0,i.jsx)(n.h3,{id:"offline-first-approach",children:"Offline-First Approach"}),"\n",(0,i.jsx)(n.p,{children:"One of RxDB's distinguishing traits is its emphasis on offline-first design. This means your jQuery application continues to function, display, and update data even when there's no network connection. When connectivity is restored, RxDB synchronizes updates with the server or other peers, ensuring consistency across all instances."}),"\n",(0,i.jsx)(n.h3,{id:"data-replication",children:"Data Replication"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB supports real-time data replication with different backends. By enabling replication, you ensure that multiple clients - be they multiple ",(0,i.jsx)(n.a,{href:"/articles/browser-database.html",children:"browser"})," tabs or separate devices - stay in sync. RxDB's conflict resolution strategies help keep the data consistent even when multiple users make changes simultaneously."]}),"\n",(0,i.jsx)(n.h3,{id:"observable-queries",children:"Observable Queries"}),"\n",(0,i.jsx)(n.p,{children:"Instead of static queries, RxDB provides observable queries. Whenever data relevant to a query changes, RxDB re-emits the new result set. You can subscribe to these updates within your jQuery code and instantly reflect them in the UI."}),"\n",(0,i.jsx)(n.h3,{id:"multi-tab-support",children:"Multi-Tab Support"}),"\n",(0,i.jsx)(n.p,{children:"Running your jQuery app in multiple tabs? RxDB automatically synchronizes changes between those tabs. Users can freely switch windows without missing real-time updates."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/multiwindow.gif",alt:"multi tab support",width:"450"})}),"\n",(0,i.jsx)(n.h3,{id:"rxdb-vs-other-jquery-database-options",children:"RxDB vs. Other jQuery Database Options"}),"\n",(0,i.jsxs)(n.p,{children:["Historically, jQuery developers might use ",(0,i.jsx)(n.code,{children:"localStorage"})," or raw ",(0,i.jsx)(n.code,{children:"IndexedDB"})," for storing data. However, these solutions can require significant boilerplate, lack reactivity, and offer no built-in sync or conflict resolution. RxDB fills these gaps with an out-of-the-box solution, abstracting away low-level database complexities and providing an event-driven, offline-capable approach."]}),"\n",(0,i.jsx)(n.h2,{id:"using-rxdb-in-a-jquery-application",children:"Using RxDB in a jQuery Application"}),"\n",(0,i.jsx)(n.h3,{id:"installing-rxdb",children:"Installing RxDB"}),"\n",(0,i.jsxs)(n.p,{children:["Install RxDB (and ",(0,i.jsx)(n.code,{children:"rxjs"}),") via npm or yarn:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(n.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"})]})})})}),"\n",(0,i.jsx)(n.p,{children:"If your project isn't set up with a build process, you can still use bundlers like Webpack or Rollup, or serve RxDB as a UMD bundle. Once included, you'll have access to RxDB globally or via import statements."}),"\n",(0,i.jsx)(n.h2,{id:"creating-and-configuring-a-database",children:"Creating and Configuring a Database"}),"\n",(0,i.jsxs)(n.p,{children:["Below is a minimal example of how to create an RxDB instance and collection. You can call this when your page initializes, then store the ",(0,i.jsx)(n.code,{children:"db"})," object for later use:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" initDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myPassword'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional encryption password"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // multi-tab support"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" eventReduce"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // optimizes event handling"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" hero"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'hero schema'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" points"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"updating-the-dom-with-jquery",children:"Updating the DOM with jQuery"}),"\n",(0,i.jsx)(n.p,{children:"Once you have your RxDB instance, you can query data reactively and use jQuery to manipulate the DOM:"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Example: Displaying heroes using jQuery"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(document)"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".ready"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" initDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Subscribing to all hero documents"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".hero"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" .$ "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// the observable"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" .subscribe"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"((heroes) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Clear the list"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" $"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'#heroList'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".empty"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Append each hero to the DOM"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".forEach"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"((hero) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" $"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'#heroList'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".append"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"
"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Example of adding a new hero"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" $"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'#addHeroBtn'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".on"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'click'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" $"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'#heroName'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".val"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroPoints"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" parseInt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"$"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'#heroPoints'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".val"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"hero"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Date"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".now"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".toString"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heroName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" points"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heroPoints"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["With this approach, any time data in the ",(0,i.jsx)(n.code,{children:"hero"})," collection changes - like when a new hero is added - your jQuery code re-renders the list of heroes automatically."]}),"\n",(0,i.jsx)(n.h2,{id:"different-rxstorage-layers-for-rxdb",children:"Different RxStorage layers for RxDB"}),"\n",(0,i.jsx)(n.p,{children:"RxDB supports multiple storage backends (RxStorage layers). Some popular ones:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage.js RxStorage"}),": Uses the browsers ",(0,i.jsx)(n.a,{href:"/articles/localstorage.html",children:"localstorage"}),". Fast and easy to set up."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),": Direct IndexedDB usage, suitable for modern browsers."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"}),": Uses the File System Access API for better performance in supported browsers."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rx-storage-memory.html",children:"Memory RxStorage"}),": Stores data in memory, handy for tests or ephemeral data."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"}),": Uses SQLite (potentially via WebAssembly). In typical browser-based scenarios, localstorage or IndexedDB storage is usually more straightforward."]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"synchronizing-data-with-rxdb-between-clients-and-servers",children:"Synchronizing Data with RxDB between Clients and Servers"}),"\n",(0,i.jsx)(n.h3,{id:"offline-first-approach-1",children:"Offline-First Approach"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB's ",(0,i.jsx)(n.a,{href:"/offline-first.html",children:"offline-first"})," approach allows your jQuery application to store and query data locally. Users can continue interacting, even offline. When connectivity returns, RxDB syncs to the server."]}),"\n",(0,i.jsx)(n.h3,{id:"conflict-resolution",children:"Conflict Resolution"}),"\n",(0,i.jsxs)(n.p,{children:["Should multiple clients update the same document, RxDB offers ",(0,i.jsx)(n.a,{href:"/transactions-conflicts-revisions.html",children:"conflict handling strategies"}),". You decide how to resolve conflicts - like keeping the latest edit or merging changes - ensuring data integrity across distributed systems."]}),"\n",(0,i.jsx)(n.h3,{id:"bidirectional-synchronization",children:"Bidirectional Synchronization"}),"\n",(0,i.jsx)(n.p,{children:"With RxDB, data changes flow both ways: from client to server and from server to client. This real-time synchronization ensures that all users or tabs see consistent, up-to-date data."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/database-replication.png",alt:"database replication",width:"200"})}),"\n",(0,i.jsx)(n.h2,{id:"advanced-rxdb-features-and-techniques",children:"Advanced RxDB Features and Techniques"}),"\n",(0,i.jsx)(n.h3,{id:"indexing-and-performance-optimization",children:"Indexing and Performance Optimization"}),"\n",(0,i.jsx)(n.p,{children:"Create indexes on frequently queried fields to speed up performance. For large data sets, indexing can drastically improve query times, keeping your jQuery UI snappy."}),"\n",(0,i.jsx)(n.h3,{id:"encryption-of-local-data",children:"Encryption of Local Data"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB supports ",(0,i.jsx)(n.a,{href:"/encryption.html",children:"encryption to secure data stored in the browser"}),". This is crucial if your application handles sensitive user information."]}),"\n",(0,i.jsx)(n.h3,{id:"change-streams-and-event-handling",children:"Change Streams and Event Handling"}),"\n",(0,i.jsxs)(n.p,{children:["Use change streams to listen for data modifications at the database or collection level. This can trigger ",(0,i.jsx)(n.a,{href:"/articles/realtime-database.html",children:"real-time"})," ",(0,i.jsx)(n.a,{href:"/articles/optimistic-ui.html",children:"UI updates"}),", notifications, or custom logic whenever the data changes."]}),"\n",(0,i.jsx)(n.h3,{id:"json-key-compression",children:"JSON Key Compression"}),"\n",(0,i.jsxs)(n.p,{children:["If your data model has large or repetitive field names, ",(0,i.jsx)(n.a,{href:"/key-compression.html",children:"JSON key compression"})," can minimize stored document size and potentially boost performance."]}),"\n",(0,i.jsx)(n.h2,{id:"best-practices-for-using-rxdb-in-jquery-applications",children:"Best Practices for Using RxDB in jQuery Applications"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Centralize Your Database: Initialize and configure RxDB in one place. Expose the instance where needed or store it globally to avoid re-creating it on every script."}),"\n",(0,i.jsx)(n.li,{children:"Leverage Observables: Instead of polling or manually refreshing data, rely on RxDB's reactivity. Subscribe to queries and let RxDB inform you when data changes."}),"\n",(0,i.jsx)(n.li,{children:"Handle Subscriptions: If you create subscriptions in a single-page context, ensure you don't re-subscribe endlessly or create memory leaks. Clean them up if you're navigating away or removing DOM elements."}),"\n",(0,i.jsx)(n.li,{children:"Offline Testing: Thoroughly test how your jQuery app behaves without a network connection. Simulate offline states in your browser's dev tools or with flight mode to ensure the user experience remains smooth."}),"\n",(0,i.jsx)(n.li,{children:"Performance Profiling: For large data sets or frequent data updates, add indexes and carefully measure query performance. Optimize only where needed."}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsx)(n.p,{children:"To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/code/",children:"RxDB GitHub Repository"}),": Visit the official GitHub repository of RxDB to access the source code, documentation, and community support."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),": Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"RxDB Examples"}),": Browse official examples to see RxDB in action and learn best practices you can apply to your own project - even if jQuery isn't explicitly featured, the patterns are similar."]}),"\n"]})]})}function p(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,n,s){s.d(n,{R:()=>o,x:()=>t});var r=s(6540);const i={},a=r.createContext(i);function o(e){const n=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),r.createElement(a.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/84a3af36.7a11f993.js b/docs/assets/js/84a3af36.7a11f993.js
deleted file mode 100644
index a3c4501cf1d..00000000000
--- a/docs/assets/js/84a3af36.7a11f993.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6861],{7916(e,s,a){a.r(s),a.d(s,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"articles/json-database","title":"RxDB - The JSON Database Built for JavaScript","description":"Experience a powerful JSON database with RxDB, built for JavaScript. Store, sync, and compress your data seamlessly across web and mobile apps.","source":"@site/docs/articles/json-database.md","sourceDirName":"articles","slug":"/articles/json-database.html","permalink":"/articles/json-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB - The JSON Database Built for JavaScript","slug":"json-database.html","description":"Experience a powerful JSON database with RxDB, built for JavaScript. Store, sync, and compress your data seamlessly across web and mobile apps."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB - Local Ionic Storage with Encryption, Compression & Sync","permalink":"/articles/ionic-storage.html"},"next":{"title":"WebSockets vs Server-Sent-Events vs Long-Polling vs WebRTC vs WebTransport","permalink":"/articles/websockets-sse-polling-webrtc-webtransport.html"}}');var r=a(4848),i=a(8453);const t={title:"RxDB - The JSON Database Built for JavaScript",slug:"json-database.html",description:"Experience a powerful JSON database with RxDB, built for JavaScript. Store, sync, and compress your data seamlessly across web and mobile apps."},o="RxDB - JSON Database for JavaScript",l={},c=[{value:"Why Choose a JSON Database?",id:"why-choose-a-json-database",level:2},{value:"Storage and Access Options for JSON Documents",id:"storage-and-access-options-for-json-documents",level:2},{value:"Compression Storage for JSON Documents",id:"compression-storage-for-json-documents",level:2},{value:"Schema Validation and Data Migration on Schema Changes",id:"schema-validation-and-data-migration-on-schema-changes",level:2},{value:"Store JSON with RxDB in Browser Applications",id:"store-json-with-rxdb-in-browser-applications",level:2},{value:"RxDB JSON Database Performance",id:"rxdb-json-database-performance",level:2},{value:"RxDB in Node.js",id:"rxdb-in-nodejs",level:2},{value:"RxDB to store JSON documents in React Native",id:"rxdb-to-store-json-documents-in-react-native",level:2},{value:"Using SQLite as a JSON Database",id:"using-sqlite-as-a-json-database",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(s.header,{children:(0,r.jsx)(s.h1,{id:"rxdb---json-database-for-javascript",children:"RxDB - JSON Database for JavaScript"})}),"\n",(0,r.jsxs)(s.p,{children:["Storing data as ",(0,r.jsx)(s.strong,{children:"JSON documents"})," in a ",(0,r.jsx)(s.strong,{children:"NoSQL"})," database is not just a trend; it's a practical choice. JSON data is highly compatible with various tools and is human-readable, making it an excellent fit for modern applications. JSON documents offer more flexibility compared to traditional SQL table rows, as they can contain nested data structures. This article introduces ",(0,r.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"}),", an open-source, flexible, performant, and battle-tested NoSQL JSON database specifically designed for ",(0,r.jsx)(s.strong,{children:"JavaScript"})," applications."]}),"\n",(0,r.jsx)("center",{children:(0,r.jsx)("a",{href:"https://rxdb.info/",children:(0,r.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JSON Database",width:"220"})})}),"\n",(0,r.jsx)(s.h2,{id:"why-choose-a-json-database",children:"Why Choose a JSON Database?"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"JavaScript Friendliness"}),": JavaScript, a prevalent language for web development, naturally uses JSON for data representation. Using a JSON database aligns seamlessly with JavaScript's native data format."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Compatibility"}),": JSON is widely supported across different programming languages and platforms. Storing data in JSON format ensures compatibility with a broad range of tools and systems. All modern programming ecosystems have packages to parse, validate and process JSON data."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Flexibility"}),": JSON documents can accommodate complex and nested data structures, allowing developers to store data in a more intuitive and hierarchical manner compared to SQL table rows. Nested data can be just stored in-document instead of having related tables."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Human-Readable"}),": JSON is easy to read and understand, simplifying debugging and data inspection tasks."]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(s.h2,{id:"storage-and-access-options-for-json-documents",children:"Storage and Access Options for JSON Documents"}),"\n",(0,r.jsx)(s.p,{children:"When incorporating JSON documents into your application, you have several storage and access options to consider:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Local In-App Database with In-Memory Storage"}),": Ideal for lightweight applications or temporary data storage, this option keeps data in memory, ensuring fast read and write operations. However, data is not persistet beyond the current application session, making it suitable for temporary data storage. With RxDB, the ",(0,r.jsx)(s.a,{href:"/rx-storage-memory.html",children:"memory RxStorage"})," can be utilized to create an in-memory database."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Local In-App Database with Persistent Storage"}),": Suitable for applications requiring data retention across sessions. Data is stored on the user's device or inside of the Node.js application, offering persistence between application sessions. It balances speed and data retention, making it versatile for various applications. With RxDB, a whole range of persistent storages is available. As example, for browser there is the ",(0,r.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB storage"}),". For server side applications, the ",(0,r.jsx)(s.a,{href:"/rx-storage-filesystem-node.html",children:"Node.js Filesystem storage"})," can be used. There are ",(0,r.jsx)(s.a,{href:"/rx-storage.html",children:"many more storages"})," for React-Native, Flutter, Capacitors.js and others."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Server Database Connected to the Application"}),": For applications requiring data synchronization and accessibility from multiple processes, a server-based database is the preferred choice. Data is stored on a ",(0,r.jsx)(s.strong,{children:"remote server"}),", facilitating data sharing, synchronization, and accessibility across multiple processes. It's suitable for scenarios requiring centralized data management and enhanced security and backup capabilities on the server. RxDB supports the ",(0,r.jsx)(s.a,{href:"/rx-storage-foundationdb.html",children:"FoundationDB"})," and ",(0,r.jsx)(s.a,{href:"/rx-storage-mongodb.html",children:"MongoDB"})," as a remote database server."]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(s.h2,{id:"compression-storage-for-json-documents",children:"Compression Storage for JSON Documents"}),"\n",(0,r.jsxs)(s.p,{children:["Compression storage for JSON documents is made effortless with RxDB's ",(0,r.jsx)(s.a,{href:"/key-compression.html",children:"key-compression plugin"}),". This feature enables the efficient storage of compressed document data, reducing storage requirements while maintaining data integrity. Queries on compressed documents remain seamless, ensuring that your application benefits from both space-saving advantages and optimal query performance, making RxDB a compelling choice for managing JSON data efficiently. The compression happens inside of the ",(0,r.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"})," and does not affect the API usage. The only limitation is that encrypted fields themself cannot be used inside a query."]}),"\n",(0,r.jsx)(s.h2,{id:"schema-validation-and-data-migration-on-schema-changes",children:"Schema Validation and Data Migration on Schema Changes"}),"\n",(0,r.jsxs)(s.p,{children:["Storing JSON documents inside of a database in an application, can cause a problem when the format of the data changes. Instead of having a single server where the data must be migrated, many client devices are out there that have to run a migration.\nWhen your application's schema evolves, RxDB provides ",(0,r.jsx)(s.a,{href:"/migration-schema.html",children:"migration strategies"})," to facilitate the transition, ensuring data consistency throughout schema updates."]}),"\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"JSONSchema Validation Plugins"}),": RxDB supports multiple ",(0,r.jsx)(s.a,{href:"/schema-validation.html",children:"JSONSchema validation plugins"}),", guaranteeing that only valid data is stored in the database. RxDB uses the JsonSchema standardization that you might know from other technologies like OpenAPI (aka Swagger)."]}),"\n",(0,r.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,r.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// RxDB Schema example"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mySchema"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- define the primary key for your documents"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- the primary key must have set maxLength"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'boolean'"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" timestamp"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" format"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'date-time'"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(s.span,{"data-line":"",children:[(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'done'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'timestamp'"}),(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,r.jsx)(s.span,{"data-line":"",children:(0,r.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,r.jsx)(s.h2,{id:"store-json-with-rxdb-in-browser-applications",children:"Store JSON with RxDB in Browser Applications"}),"\n",(0,r.jsx)(s.p,{children:"RxDB offers versatile storage solutions for browser-based applications:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Multiple Storage Plugins"}),": RxDB supports various storage backends, including ",(0,r.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"}),", ",(0,r.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"localstorage"})," and ",(0,r.jsx)(s.a,{href:"/rx-storage-memory.html",children:"In-Memory"}),", catering to a range of browser environments."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Observable Queries"}),": With RxDB, you can create observable ",(0,r.jsx)(s.a,{href:"/rx-query.html",children:"queries"})," that work seamlessly across multiple browser tabs, providing real-time updates and synchronization."]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)("p",{align:"center",children:(0,r.jsx)("img",{src:"../files/multiwindow.gif",alt:"multi tab support",width:"450"})}),"\n",(0,r.jsx)(s.h2,{id:"rxdb-json-database-performance",children:"RxDB JSON Database Performance"}),"\n",(0,r.jsx)(s.p,{children:"Certainly! Let's delve deeper into the performance aspects of RxDB when it comes to working with JSON data."}),"\n",(0,r.jsxs)(s.ol,{children:["\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Efficient Querying:"})," RxDB is engineered for rapid and efficient querying of JSON data. It employs a well-optimized indexing system that allows for lightning-fast retrieval of specific data points within your JSON documents. Whether you're fetching individual values or complex nested structures, RxDB's query performance is designed to keep your application responsive, even when dealing with large datasets."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Scalability:"})," As your application grows and your ",(0,r.jsx)(s.a,{href:"/articles/json-based-database.html",children:"JSON dataset"})," expands, RxDB scales gracefully. Its performance remains consistent, enabling you to handle increasingly larger volumes of data without compromising on speed or responsiveness. This scalability is essential for applications that need to accommodate growing user bases and evolving data needs."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"Reduced Latency:"})," RxDB's streamlined data access mechanisms significantly reduce latency when working with JSON data. Whether you're reading from the database, making updates, or synchronizing data between clients and servers, RxDB's optimized operations help minimize the delays often associated with data access. Observed queries are optimized with the ",(0,r.jsx)(s.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce algorithm"})," to provide nearly-instand UI updates on data changes."]}),"\n"]}),"\n",(0,r.jsxs)(s.li,{children:["\n",(0,r.jsxs)(s.p,{children:[(0,r.jsx)(s.strong,{children:"RxStorage Layer"}),": Because RxDB allows you to swap out the storage layer. A storage with the most optimal performance can be chosen for each runtime while not touching other database code. Depending on the access patterns, you can pick exactly the storage that is best:"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)("p",{align:"center",children:(0,r.jsx)("img",{src:"../files/rx-storage-performance-browser.png",alt:"RxStorage performance - browser",width:"700"})}),"\n",(0,r.jsx)(s.h2,{id:"rxdb-in-nodejs",children:"RxDB in Node.js"}),"\n",(0,r.jsxs)(s.p,{children:["Node.js developers can also benefit from RxDB's capabilities. By integrating RxDB into your Node.js applications, you can harness the power of a NoSQL JSON db to efficiently manage your data on the server-side. RxDB's flexibility, performance, and essential features are equally valuable in server-side development. ",(0,r.jsx)(s.a,{href:"/nodejs-database.html",children:"Read more about RxDB+Node.js"}),"."]}),"\n",(0,r.jsx)(s.h2,{id:"rxdb-to-store-json-documents-in-react-native",children:"RxDB to store JSON documents in React Native"}),"\n",(0,r.jsxs)(s.p,{children:["For mobile app developers working with React Native, RxDB offers a convenient solution for handling JSON data. Whether you're building Android or iOS applications, RxDB's compatibility with JavaScript and its ability to work with JSON documents make it a natural choice for data management within your React Native apps. ",(0,r.jsx)(s.a,{href:"/react-native-database.html",children:"Read more about RxDB+React-Native"}),"."]}),"\n",(0,r.jsx)(s.h2,{id:"using-sqlite-as-a-json-database",children:"Using SQLite as a JSON Database"}),"\n",(0,r.jsxs)(s.p,{children:["In some cases, you might want to use SQLite as a backend storage solution for your JSON data. RxDB can be configured ",(0,r.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"to work with SQLite"}),", providing the benefits of both a relational database system and JSON document storage. This hybrid approach can be advantageous when dealing with complex data relationships while retaining the flexibility of JSON data representation."]}),"\n",(0,r.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,r.jsx)(s.p,{children:"To further explore RxDB and get started with using it in your frontend applications, consider the following resources:"}),"\n",(0,r.jsxs)(s.ul,{children:["\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),": A step-by-step guide to quickly set up RxDB in your project and start leveraging its features."]}),"\n",(0,r.jsxs)(s.li,{children:[(0,r.jsx)(s.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB GitHub Repository"}),": The official repository for RxDB, where you can find the code, examples, and community support."]}),"\n"]}),"\n",(0,r.jsxs)(s.p,{children:["By embracing ",(0,r.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," as your ",(0,r.jsx)(s.strong,{children:"JSON database"})," solution, you can tap into the extensive capabilities of JSON data storage. This empowers your applications with offline accessibility, caching, enhanced performance, and effortless data synchronization. RxDB's focus on JavaScript and its robust feature set render it the perfect selection for frontend developers in pursuit of efficient and scalable data storage solutions."]})]})}function h(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,r.jsx)(s,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},8453(e,s,a){a.d(s,{R:()=>t,x:()=>o});var n=a(6540);const r={},i=n.createContext(r);function t(e){const s=n.useContext(i);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function o(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:t(e.components),n.createElement(i.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/84ae55a4.1bdc94ff.js b/docs/assets/js/84ae55a4.1bdc94ff.js
deleted file mode 100644
index f843e1fba3b..00000000000
--- a/docs/assets/js/84ae55a4.1bdc94ff.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[588],{3247(e,n,s){s.d(n,{g:()=>r});var i=s(4848);function r(e){const n=[];let s=null;return e.children.forEach(e=>{e.props.id?(s&&n.push(s),s={headline:e,paragraphs:[]}):s&&s.paragraphs.push(e)}),s&&n.push(s),(0,i.jsx)("div",{style:t.stepsContainer,children:n.map((e,n)=>(0,i.jsxs)("div",{style:t.stepWrapper,children:[(0,i.jsxs)("div",{style:t.stepIndicator,children:[(0,i.jsx)("div",{style:t.stepNumber,children:n+1}),(0,i.jsx)("div",{style:t.verticalLine})]}),(0,i.jsxs)("div",{style:t.stepContent,children:[(0,i.jsx)("div",{children:e.headline}),e.paragraphs.map((e,n)=>(0,i.jsx)("div",{style:t.item,children:e},n))]})]},n))})}const t={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},7465(e,n,s){s.r(n),s.d(n,{assets:()=>c,contentTitle:()=>o,default:()=>p,frontMatter:()=>l,metadata:()=>i,toc:()=>d});const i=JSON.parse('{"id":"replication-nats","title":"RxDB & NATS - Realtime Sync","description":"Seamlessly sync your RxDB data with NATS for real-time, two-way replication. Handle conflicts, errors, and retries with ease.","source":"@site/docs/replication-nats.md","sourceDirName":".","slug":"/replication-nats.html","permalink":"/replication-nats.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB & NATS - Realtime Sync","slug":"replication-nats.html","description":"Seamlessly sync your RxDB data with NATS for real-time, two-way replication. Handle conflicts, errors, and retries with ease."},"sidebar":"tutorialSidebar","previous":{"title":"Supabase Replication","permalink":"/replication-supabase.html"},"next":{"title":"Appwrite Replication","permalink":"/replication-appwrite.html"}}');var r=s(4848),t=s(8453),a=s(3247);const l={title:"RxDB & NATS - Realtime Sync",slug:"replication-nats.html",description:"Seamlessly sync your RxDB data with NATS for real-time, two-way replication. Handle conflicts, errors, and retries with ease."},o="Replication with NATS",c={},d=[{value:"Precondition",id:"precondition",level:2},{value:"Usage",id:"usage",level:2},{value:"Install the nats package",id:"install-the-nats-package",level:3},{value:"Start the Replication",id:"start-the-replication",level:3},{value:"Handling deletes",id:"handling-deletes",level:2}];function h(e){const n={a:"a",code:"code",em:"em",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",span:"span",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"replication-with-nats",children:"Replication with NATS"})}),"\n",(0,r.jsxs)(n.p,{children:["With this RxDB plugin you can run a two-way realtime replication with a ",(0,r.jsx)(n.a,{href:"https://nats.io/",children:"NATS"})," server."]}),"\n",(0,r.jsxs)(n.p,{children:["The replication itself uses the ",(0,r.jsx)(n.a,{href:"/replication.html",children:"RxDB Sync Engine"})," which handles conflicts, errors and retries.\nOn the client side the official ",(0,r.jsx)(n.a,{href:"https://www.npmjs.com/package/nats",children:"NATS npm package"})," is used to connect to the NATS server."]}),"\n",(0,r.jsx)(n.p,{children:"NATS is a messaging system that by itself does not have a validation or granulary access control build in.\nTherefore it is not recommended to directly replicate the NATS server with an untrusted RxDB client application. Instead you should replicated from NATS to your Node.js server side RxDB database."}),"\n",(0,r.jsx)(n.h2,{id:"precondition",children:"Precondition"}),"\n",(0,r.jsxs)(n.p,{children:["For the replication endpoint the NATS cluster must have enabled ",(0,r.jsx)(n.a,{href:"https://docs.nats.io/nats-concepts/jetstream",children:"JetStream"})," and store all message data as ",(0,r.jsx)(n.a,{href:"https://docs.nats.io/using-nats/developer/sending/structure",children:"structured JSON"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"The easiest way to start a compatible NATS server is to use the official docker image:"}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.code,{children:"docker run --rm --name rxdb-nats -p 4222:4222 nats:2.9.17 -js"})}),"\n",(0,r.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,r.jsxs)(a.g,{children:[(0,r.jsx)(n.h3,{id:"install-the-nats-package",children:"Install the nats package"}),(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,r.jsx)(n.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" nats"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string)"},children:" --save"})]})})})}),(0,r.jsx)(n.h3,{id:"start-the-replication",children:"Start the Replication"}),(0,r.jsxs)(n.p,{children:["To start the replication, import the ",(0,r.jsx)(n.code,{children:"replicateNats()"})," method from the RxDB plugin and call it with the collection\nthat must be replicated.\nThe replication runs ",(0,r.jsx)(n.em,{children:"per RxCollection"}),", you can replicate multiple RxCollections by starting a new replication for each of them."]}),(0,r.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,r.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,r.jsxs)(n.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicateNats"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-nats'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateNats"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-nats-replication-collection-A'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // in NATS, each stream need a name"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" streamName"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'stream-for-replication-A'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The subject prefix determines how the documents are stored in NATS."})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * For example the document with id 'alice'"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * will have the subject 'foobar.alice'"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" subjectPrefix"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" connection"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { servers"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'localhost:4222'"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 30"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,r.jsxs)(n.span,{"data-line":"",children:[(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,r.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 30"})]}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,r.jsx)(n.span,{"data-line":"",children:(0,r.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]}),"\n",(0,r.jsx)(n.h2,{id:"handling-deletes",children:"Handling deletes"}),"\n",(0,r.jsxs)(n.p,{children:["RxDB requires you to never ",(0,r.jsx)(n.a,{href:"/replication.html#data-layout-on-the-server",children:"fully delete documents"}),". This is needed to be able to replicate the deletion state of a document to other instances. The NATS replication will set a boolean ",(0,r.jsx)(n.code,{children:"_deleted"})," field to all documents to indicate the deletion state. You can change this by setting a different ",(0,r.jsx)(n.code,{children:"deletedField"})," in the sync options."]})]})}function p(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(h,{...e})}):h(e)}},8453(e,n,s){s.d(n,{R:()=>a,x:()=>l});var i=s(6540);const r={},t=i.createContext(r);function a(e){const n=i.useContext(t);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),i.createElement(t.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/85caacef.9966ef6e.js b/docs/assets/js/85caacef.9966ef6e.js
deleted file mode 100644
index 2cc3798eb49..00000000000
--- a/docs/assets/js/85caacef.9966ef6e.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3015],{8496(e,a,s){s.r(a),s.d(a,{default:()=>l});var t=s(544),i=s(4848);function l(){return(0,t.default)({sem:{id:"gads",metaTitle:"The local Database for Expo",appName:"Expo",title:(0,i.jsxs)(i.Fragment,{children:["The easiest way to ",(0,i.jsx)("b",{children:"store"})," and ",(0,i.jsx)("b",{children:"sync"})," Data in Expo"]}),iconUrl:"/files/icons/expo_white.svg"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/86b4e356.9be07523.js b/docs/assets/js/86b4e356.9be07523.js
deleted file mode 100644
index c656c352235..00000000000
--- a/docs/assets/js/86b4e356.9be07523.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2786],{2344(s,n,e){e.r(n),e.d(n,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"orm","title":"ORM","description":"Like mongoose, RxDB has ORM-capabilities which can be used to add specific behavior to documents and collections.","source":"@site/docs/orm.md","sourceDirName":".","slug":"/orm.html","permalink":"/orm.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"ORM","slug":"orm.html"},"sidebar":"tutorialSidebar","previous":{"title":"Population","permalink":"/population.html"},"next":{"title":"Fulltext Search \ud83d\udc51","permalink":"/fulltext-search.html"}}');var o=e(4848),i=e(8453);const a={title:"ORM",slug:"orm.html"},l="Object-Data-Relational-Mapping",t={},c=[{value:"statics",id:"statics",level:2},{value:"Add statics to a collection",id:"add-statics-to-a-collection",level:3},{value:"instance-methods",id:"instance-methods",level:2},{value:"Add instance-methods to a collection",id:"add-instance-methods-to-a-collection",level:3},{value:"attachment-methods",id:"attachment-methods",level:2}];function d(s){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",span:"span",...(0,i.R)(),...s.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"object-data-relational-mapping",children:"Object-Data-Relational-Mapping"})}),"\n",(0,o.jsxs)(n.p,{children:["Like ",(0,o.jsx)(n.a,{href:"http://mongoosejs.com/docs/guide.html#methods",children:"mongoose"}),", RxDB has ORM-capabilities which can be used to add specific behavior to documents and collections."]}),"\n",(0,o.jsx)(n.h2,{id:"statics",children:"statics"}),"\n",(0,o.jsx)(n.p,{children:"Statics are defined collection-wide and can be called on the collection."}),"\n",(0,o.jsx)(n.h3,{id:"add-statics-to-a-collection",children:"Add statics to a collection"}),"\n",(0,o.jsxs)(n.p,{children:["To add static functions, pass a ",(0,o.jsx)(n.code,{children:"statics"}),"-object when you create your collection. The object contains functions, mapped to their function-names."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" statics"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" scream"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(){"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'AAAH!!'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".scream"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'AAAH!!'"})})]})})}),"\n",(0,o.jsx)(n.p,{children:"You can also use the this-keyword which resolves to the collection:"}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" statics"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" whoAmI"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(){"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".name;"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".whoAmI"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'heroes'"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"instance-methods",children:"instance-methods"}),"\n",(0,o.jsx)(n.p,{children:"Instance-methods are defined collection-wide. They can be called on the RxDocuments of the collection."}),"\n",(0,o.jsx)(n.h3,{id:"add-instance-methods-to-a-collection",children:"Add instance-methods to a collection"}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" methods"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" scream"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(){"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'AAAH!!'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".scream"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'AAAH!!'"})})]})})}),"\n",(0,o.jsx)(n.p,{children:"Here you can also use the this-keyword:"}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" methods"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" whoAmI"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(){"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'I am '"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".name "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '!!'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Skeletor'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".whoAmI"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'I am Skeletor!!'"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"attachment-methods",children:"attachment-methods"}),"\n",(0,o.jsx)(n.p,{children:"Attachment-methods are defined collection-wide. They can be called on the RxAttachments of the RxDocuments of the collection."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" scream"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(){"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'AAAH!!'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".putAttachment"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'cat.txt'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" data"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'meow I am a kitty'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/plain'"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"attachment"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".scream"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'AAAH!!'"})})]})})})]})}function h(s={}){const{wrapper:n}={...(0,i.R)(),...s.components};return n?(0,o.jsx)(n,{...s,children:(0,o.jsx)(d,{...s})}):d(s)}},8453(s,n,e){e.d(n,{R:()=>a,x:()=>l});var r=e(6540);const o={},i=r.createContext(o);function a(s){const n=r.useContext(i);return r.useMemo(function(){return"function"==typeof s?s(n):{...n,...s}},[n,s])}function l(s){let n;return n=s.disableParentContext?"function"==typeof s.components?s.components(o):s.components||o:a(s.components),r.createElement(i.Provider,{value:n},s.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8a22f3a9.87a88033.js b/docs/assets/js/8a22f3a9.87a88033.js
deleted file mode 100644
index b170d949723..00000000000
--- a/docs/assets/js/8a22f3a9.87a88033.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9257],{2159(e,t,a){a.r(t),a.d(t,{assets:()=>u,contentTitle:()=>h,default:()=>b,frontMatter:()=>d,metadata:()=>n,toc:()=>m});const n=JSON.parse('{"id":"errors","title":"Error Messages","description":"Learn how RxDB throws RxErrors with codes and parameters. Keep builds lean, yet unveil full messages in development via the DevMode plugin.","source":"@site/docs/errors.md","sourceDirName":".","slug":"/errors.html","permalink":"/errors.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Error Messages","slug":"errors.html","description":"Learn how RxDB throws RxErrors with codes and parameters. Keep builds lean, yet unveil full messages in development via the DevMode plugin."},"sidebar":"tutorialSidebar","previous":{"title":"Creating Plugins","permalink":"/plugins.html"},"next":{"title":"Schema Migration","permalink":"/migration-schema.html"}}');var o=a(4848),s=a(8453);const i={UT1:"given name is no string or empty",UT2:"collection- and database-names must match the regex to be compatible with couchdb databases.\n See https://neighbourhood.ie/blog/2020/10/13/everything-you-need-to-know-about-couchdb-database-names/\n info: if your database-name specifies a folder, the name must contain the slash-char '/' or '\\'",UT3:"replication-direction must either be push or pull or both. But not none",UT4:"given leveldown is no valid adapter",UT5:"keyCompression is set to true in the schema but no key-compression handler is used in the storage",UT6:"schema contains encrypted fields but no encryption handler is used in the storage",UT7:"attachments.compression is enabled but no attachment-compression plugin is used",UT8:"crypto.subtle.digest is not available in your runtime. For expo/react-native see https://discord.com/channels/969553741705539624/1341392686267109458/1343639513850843217 ",PL1:"Given plugin is not RxDB plugin.",PL3:"A plugin with the same name was already added but it was not the exact same JavaScript object",P2:"bulkWrite() cannot be called with an empty array",QU1:"RxQuery._execOverDatabase(): op not known",QU4:"RxQuery.regex(): You cannot use .regex() on the primary field",QU5:"RxQuery.sort(): does not work because key is not defined in the schema",QU6:"RxQuery.limit(): cannot be called on .findOne()",QU9:"throwIfMissing can only be used in findOne queries",QU10:"result empty and throwIfMissing: true",QU11:"RxQuery: no valid query params given",QU12:"Given index is not in schema",QU13:"A top level field of the query is not included in the schema",QU14:"Running a count() query in slow mode is now allowed. Either run a count() query with a selector that fully matches an index or set allowSlowCount=true when calling the createRxDatabase",QU15:"For count queries it is not allowed to use skip or limit",QU16:"$regex queries must be defined by a string, not an RegExp instance. This is because RegExp objects cannot be JSON stringified and also they are mutable which would be dangerous",QU17:"Chained queries cannot be used on findByIds() RxQuery instances",QU18:"Malformed query result data. This likely happens because you create a OPFS-storage RxDatabase inside of a worker but did not set the usesRxDatabaseInWorker setting. https://rxdb.info/rx-storage-opfs.html?console=opfs#setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker ",QU19:"Queries must not contain fields or properties with the value `undefined`: https://github.com/pubkey/rxdb/issues/6792#issuecomment-2624555824 ",MQ1:"path must be a string or object",MQ2:"Invalid argument",MQ3:"Invalid sort() argument. Must be a string, object, or array",MQ4:"Invalid argument. Expected instanceof mquery or plain object",MQ5:"method must be used after where() when called with these arguments",MQ6:"Can't mix sort syntaxes. Use either array or object | .sort([['field', 1], ['test', -1]]) | .sort({ field: 1, test: -1 })",MQ7:"Invalid sort value",MQ8:"Can't mix sort syntaxes. Use either array or object",DB1:"RxDocument.prepare(): another instance on this adapter has a different password",DB2:"RxDatabase.addCollections(): collection-names cannot start with underscore _",DB3:"RxDatabase.addCollections(): collection already exists. use myDatabase[collectionName] to get it",DB4:"RxDatabase.addCollections(): schema is missing",DB5:"RxDatabase.addCollections(): collection-name not allowed",DB6:"RxDatabase.addCollections(): another instance created this collection with a different schema. Read thishttps://rxdb.info/rx-schema.html?console=qa#faq ",DB8:'createRxDatabase(): A RxDatabase with the same name and adapter already exists.\nMake sure to use this combination of storage+databaseName only once\nIf you have the duplicate database on purpose to simulate multi-tab behavior in unit tests, set "ignoreDuplicate: true".\nAs alternative you can set "closeDuplicates: true" like if this happens in your react projects with hot reload that reloads the code without reloading the process.',DB9:"ignoreDuplicate is only allowed in dev-mode and must never be used in production",DB11:"createRxDatabase(): Invalid db-name, folder-paths must not have an ending slash",DB12:"RxDatabase.addCollections(): could not write to internal store",DB13:"createRxDatabase(): Invalid db-name or collection name, name contains the dollar sign",DB14:"no custom reactivity factory added on database creation",COL1:"RxDocument.insert() You cannot insert an existing document",COL2:"RxCollection.insert() fieldName ._id can only be used as primaryKey",COL3:"RxCollection.upsert() does not work without primary",COL4:"RxCollection.incrementalUpsert() does not work without primary",COL5:"RxCollection.find() if you want to search by _id, use .findOne(_id)",COL6:"RxCollection.findOne() needs a queryObject or string. Notice that in RxDB, primary keys must be strings and cannot be numbers.",COL7:"hook must be a function",COL8:"hooks-when not known",COL9:"RxCollection.addHook() hook-name not known",COL10:"RxCollection .postCreate-hooks cannot be async",COL11:"migrationStrategies must be an object",COL12:"A migrationStrategy is missing or too much",COL13:"migrationStrategy must be a function",COL14:"given static method-name is not a string",COL15:"static method-names cannot start with underscore _",COL16:"given static method is not a function",COL17:"RxCollection.ORM: statics-name not allowed",COL18:"collection-method not allowed because fieldname is in the schema",COL20:"Storage write error",COL21:"The RxCollection is closed or removed already, either from this JavaScript realm or from another, like a browser tab",CONFLICT:"Document update conflict. When changing a document you must work on the previous revision",COL22:".bulkInsert() and .bulkUpsert() cannot be run with multiple documents that have the same primary key",COL23:"In the open-source version of RxDB, the amount of collections that can exist in parallel is limited to 16. If you already purchased the premium access, you can remove this limit: https://rxdb.info/rx-collection.html?console=limit#faq",DOC1:"RxDocument.get$ cannot get observable of in-array fields because order cannot be guessed",DOC2:"cannot observe primary path",DOC3:"final fields cannot be observed",DOC4:"RxDocument.get$ cannot observe a non-existed field",DOC5:"RxDocument.populate() cannot populate a non-existed field",DOC6:"RxDocument.populate() cannot populate because path has no ref",DOC7:"RxDocument.populate() ref-collection not in database",DOC8:"RxDocument.set(): primary-key cannot be modified",DOC9:"final fields cannot be modified",DOC10:"RxDocument.set(): cannot set childpath when rootPath not selected",DOC11:"RxDocument.save(): can't save deleted document",DOC13:"RxDocument.remove(): Document is already deleted",DOC14:"RxDocument.close() does not exist",DOC15:"query cannot be an array",DOC16:"Since version 8.0.0 RxDocument.set() can only be called on temporary RxDocuments",DOC17:"Since version 8.0.0 RxDocument.save() can only be called on non-temporary documents",DOC18:"Document property for composed primary key is missing",DOC19:"Value of primary key(s) cannot be changed",DOC20:"PrimaryKey missing",DOC21:"PrimaryKey must be equal to PrimaryKey.trim(). It cannot start or end with a whitespace",DOC22:"PrimaryKey must not contain a linebreak",DOC23:'PrimaryKey must not contain a double-quote ["]',DOC24:"Given document data could not be structured cloned. This happens if you pass non-plain-json data into it, like a Date() object or a Function. In vue.js this happens if you use ref() on the document data which transforms it into a Proxy object.",DM1:"migrate() Migration has already run",DM2:"migration of document failed final document does not match final schema",DM3:"migration already running",DM4:"Migration errored",DM5:"Cannot open database state with newer RxDB version. You have to migrate your database state first. See https://rxdb.info/migration-storage.html?console=storage ",AT1:"to use attachments, please define this in your schema",EN1:"password is not valid",EN2:"validatePassword: min-length of password not complied",EN3:"Schema contains encrypted properties but no password is given",EN4:"Password not valid",JD1:"You must create the collections before you can import their data",JD2:"RxCollection.importJSON(): the imported json relies on a different schema",JD3:"RxCollection.importJSON(): json.passwordHash does not match the own",LD1:"RxDocument.allAttachments$ can't use attachments on local documents",LD2:"RxDocument.get(): objPath must be a string",LD3:"RxDocument.get$ cannot get observable of in-array fields because order cannot be guessed",LD4:"cannot observe primary path",LD5:"RxDocument.set() id cannot be modified",LD6:"LocalDocument: Function is not usable on local documents",LD7:"Local document already exists",LD8:"localDocuments not activated. Set localDocuments=true on creation, when you want to store local documents on the RxDatabase or RxCollection.",RC1:"Replication: already added",RC2:"replicateCouchDB() query must be from the same RxCollection",RC4:"RxCouchDBReplicationState.awaitInitialReplication() cannot await initial replication when live: true",RC5:"RxCouchDBReplicationState.awaitInitialReplication() cannot await initial replication if multiInstance because the replication might run on another instance",RC6:"syncFirestore() serverTimestampField MUST NOT be part of the collections schema and MUST NOT be nested.",RC7:"SimplePeer requires to have process.nextTick() polyfilled, see https://rxdb.info/replication-webrtc.html?console=webrtc ",RC_PULL:"RxReplication pull handler threw an error - see .errors for more details",RC_STREAM:"RxReplication pull stream$ threw an error - see .errors for more details",RC_PUSH:"RxReplication push handler threw an error - see .errors for more details",RC_PUSH_NO_AR:"RxReplication push handler did not return an array with the conflicts",RC_WEBRTC_PEER:"RxReplication WebRTC Peer has error",RC_COUCHDB_1:"replicateCouchDB() url must end with a slash like 'https://example.com/mydatabase/'",RC_COUCHDB_2:"replicateCouchDB() did not get valid result with rows.",RC_OUTDATED:"Outdated client, update required. Replication was canceled",RC_UNAUTHORIZED:"Unauthorized client, update the replicationState.headers to set correct auth data",RC_FORBIDDEN:"Client behaves wrong so the replication was canceled. Mostly happens if the client tries to write data that it is not allowed to",SC1:"fieldnames do not match the regex",SC2:"SchemaCheck: name 'item' reserved for array-fields",SC3:"SchemaCheck: fieldname has a ref-array but items-type is not string",SC4:"SchemaCheck: fieldname has a ref but is not type string, [string,null] or array",SC6:"SchemaCheck: primary can only be defined at top-level",SC7:"SchemaCheck: default-values can only be defined at top-level",SC8:"SchemaCheck: first level-fields cannot start with underscore _",SC10:"SchemaCheck: schema defines ._rev, this will be done automatically",SC11:"SchemaCheck: schema needs a number >=0 as version",SC13:"SchemaCheck: primary is always index, do not declare it as index",SC14:"SchemaCheck: primary is always unique, do not declare it as index",SC15:"SchemaCheck: primary cannot be encrypted",SC16:"SchemaCheck: primary must have type: string",SC17:"SchemaCheck: top-level fieldname is not allowed. See https://rxdb.info/rx-schema.html?console=toplevel#non-allowed-properties ",SC18:"SchemaCheck: indexes must be an array",SC19:"SchemaCheck: indexes must contain strings or arrays of strings",SC20:"SchemaCheck: indexes.array must contain strings",SC21:"SchemaCheck: given index is not defined in schema",SC22:"SchemaCheck: given indexKey is not type:string",SC23:"SchemaCheck: fieldname is not allowed",SC24:"SchemaCheck: required fields must be set via array. See https://spacetelescope.github.io/understanding-json-schema/reference/object.html#required",SC25:"SchemaCheck: compoundIndexes needs to be specified in the indexes field",SC26:"SchemaCheck: indexes needs to be specified at collection schema level",SC28:"SchemaCheck: encrypted fields is not defined in the schema",SC29:"SchemaCheck: missing object key 'properties'",SC30:"SchemaCheck: primaryKey is required",SC32:"SchemaCheck: primary field must have the type string/number/integer",SC33:"SchemaCheck: used primary key is not a property in the schema",SC34:"Fields of type string that are used in an index, must have set the maxLength attribute in the schema",SC35:"Fields of type number/integer that are used in an index, must have set the multipleOf attribute in the schema",SC36:"A field of this type cannot be used as index",SC37:"Fields of type number that are used in an index, must have set the minimum and maximum attribute in the schema",SC38:"Fields of type boolean that are used in an index, must be required in the schema",SC39:"The primary key must have the maxLength attribute set. Ensure you use the dev-mode plugin when developing with RxDB.",SC40:"$ref fields in the schema are not allowed. RxDB cannot resolve related schemas because it would have a negative performance impact.It would have to run http requests on runtime. $ref fields should be resolved during build time.",SC41:"minimum, maximum and maxLength values for indexes must be real numbers, not Infinity or -Infinity",SC42:"Primary key and also indexed fields which are strings, must have a maxLength that is <= 2048. Notice that having a big maxLength can negatively affect the performance. Only set it as big as it has to be.",DVM1:"When dev-mode is enabled, your storage must use one of the schema validators at the top level. This is because most problems people have with RxDB is because they store data that is not valid to the schema which causes strange bugs and problems.",VD1:"Sub-schema not found, does the schemaPath exists in your schema?",VD2:"object does not match schema",S1:"You cannot create collections after calling RxDatabase.server()",GQL1:"GraphQL replication: cannot find sub schema by key",GQL3:"GraphQL replication: pull returns more documents then batchSize",CRDT1:"CRDT operations cannot be used because the crdt options are not set in the schema.",CRDT2:"RxDocument.incrementalModify() cannot be used when CRDTs are activated.",CRDT3:"To use CRDTs you MUST NOT set a conflictHandler because the default CRDT conflict handler must be used",DXE1:"non-required index fields are not possible with the dexie.js RxStorage: https://github.com/pubkey/rxdb/pull/6643#issuecomment-2505310082",SQL1:"The trial version of the SQLite storage does not support attachments.",SQL2:"The trial version of the SQLite storage is limited to contain 300 documents",SQL3:"The trial version of the SQLite storage is limited to running 500 operations",RM1:"Cannot communicate with a remote that was build on a different RxDB version. Did you forget to rebuild your workers when updating RxDB?",MG1:"If _id is used as primaryKey, all documents in the MongoDB instance must have a string-value as _id, not an ObjectId or number",R1:"You must provide a valid RxDatabase to the the RxDatabaseProvider",R2:"Could not find database in context, please ensure the component is wrapped in a ",R3:"The provided value for the collection parameter is not a valid RxCollection",SNH:"This should never happen"};var r=a(6347);const c=Object.entries(i);function l(){const e=(0,r.zy)();console.dir(i);const t={"":{boxSizing:"border-box"},ul:{listStyleType:"none"},li:{borderStyle:"solid",borderWidth:1,height:"auto",borderColor:"var(--color-top)",padding:32,borderRadius:14,backgroundColor:"var(--bg-color-dark)",justifyContent:"space-between",gap:48,margin:10,marginBottom:20},liHighlight:{boxShadow:"2px 2px 13px var(--color-top), -2px -1px 14px var(--color-top)"},errorCode:{fontSize:"1.3em",lineHeight:"130%",margin:"0 0 8px"},innerUl:{marginTop:14}};return(0,o.jsx)("ul",{style:t.ul,children:c.map(([a,n])=>{return(0,o.jsxs)("li",{id:a,style:e.hash==="#"+a?{...t.li,...t.liHighlight}:t.li,children:[(0,o.jsxs)("h6",{style:t.errorCode,children:["Code: ",(0,o.jsx)("a",{href:"#"+a,children:a})]}),(s=n,(s+="").charAt(0).toUpperCase()+s.substr(1)),(0,o.jsxs)("ul",{style:t.innerUl,children:[(0,o.jsx)("li",{children:(0,o.jsx)("a",{href:"https://github.com/pubkey/rxdb/search?q="+a+"&type=code",target:"_blank",children:"Search In Code"})}),(0,o.jsx)("li",{children:(0,o.jsx)("a",{href:"https://github.com/pubkey/rxdb/search?q="+a+"&type=issues",target:"_blank",children:"Search In Issues"})}),(0,o.jsx)("li",{children:(0,o.jsx)("a",{href:"/chat/",target:"_blank",children:"Search In Chat"})})]})]},a);var s})})}const d={title:"Error Messages",slug:"errors.html",description:"Learn how RxDB throws RxErrors with codes and parameters. Keep builds lean, yet unveil full messages in development via the DevMode plugin."},h="RxDB Error Messages",u={},m=[{value:"All RxDB error messages",id:"all-rxdb-error-messages",level:2}];function p(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",header:"header",p:"p",...(0,s.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.header,{children:(0,o.jsx)(t.h1,{id:"rxdb-error-messages",children:"RxDB Error Messages"})}),"\n",(0,o.jsxs)(t.p,{children:["When RxDB has an error, an ",(0,o.jsx)(t.code,{children:"RxError"})," object is thrown instead of a normal JavaScript ",(0,o.jsx)(t.code,{children:"Error"}),". This ",(0,o.jsx)(t.code,{children:"RxError"})," contains additional properties such as a ",(0,o.jsx)(t.code,{children:"code"})," field and ",(0,o.jsx)(t.code,{children:"parameters"}),". By default the full human readable error messages are not included into the RxDB build. This is because error messages have a high entropy and cannot be compressed well. Therefore only an error message with the correct error-code and parameters is thrown but without the full text.\nWhen you enable the ",(0,o.jsx)(t.a,{href:"/dev-mode.html",children:"DevMode Plugin"})," the full error messages are added to the ",(0,o.jsx)(t.code,{children:"RxError"}),". This should only be done in development, not in production builds to keep a small build size."]}),"\n",(0,o.jsx)(t.h2,{id:"all-rxdb-error-messages",children:"All RxDB error messages"}),"\n","\n",(0,o.jsx)(l,{})]})}function b(e={}){const{wrapper:t}={...(0,s.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(p,{...e})}):p(e)}},8453(e,t,a){a.d(t,{R:()=>i,x:()=>r});var n=a(6540);const o={},s=n.createContext(o);function i(e){const t=n.useContext(s);return n.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function r(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),n.createElement(s.Provider,{value:t},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8a442806.cda40cae.js b/docs/assets/js/8a442806.cda40cae.js
deleted file mode 100644
index beb8306683e..00000000000
--- a/docs/assets/js/8a442806.cda40cae.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1054],{8453(e,n,s){s.d(n,{R:()=>a,x:()=>l});var r=s(6540);const i={},t=r.createContext(i);function a(e){const n=r.useContext(t);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(t.Provider,{value:n},e.children)}},9770(e,n,s){s.r(n),s.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>d,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"fulltext-search","title":"Fulltext Search \ud83d\udc51","description":"Master local fulltext search with RxDB\'s FlexSearch plugin. Enjoy real-time indexing, efficient queries, and offline-first support made easy.","source":"@site/docs/fulltext-search.md","sourceDirName":".","slug":"/fulltext-search.html","permalink":"/fulltext-search.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Fulltext Search \ud83d\udc51","slug":"fulltext-search.html","description":"Master local fulltext search with RxDB\'s FlexSearch plugin. Enjoy real-time indexing, efficient queries, and offline-first support made easy."},"sidebar":"tutorialSidebar","previous":{"title":"ORM","permalink":"/orm.html"},"next":{"title":"Vector Database","permalink":"/articles/javascript-vector-database.html"}}');var i=s(4848),t=s(8453);const a={title:"Fulltext Search \ud83d\udc51",slug:"fulltext-search.html",description:"Master local fulltext search with RxDB's FlexSearch plugin. Enjoy real-time indexing, efficient queries, and offline-first support made easy."},l="Fulltext Search",o={},c=[{value:"Benefits of using a local fulltext search",id:"benefits-of-using-a-local-fulltext-search",level:2},{value:"Using the RxDB Fulltext Search",id:"using-the-rxdb-fulltext-search",level:2}];function h(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",...(0,t.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"fulltext-search",children:"Fulltext Search"})}),"\n",(0,i.jsxs)(n.p,{children:["To run fulltext search queries on the local data, RxDB has a fulltext search plugin based on ",(0,i.jsx)(n.a,{href:"https://github.com/nextapps-de/flexsearch",children:"flexsearch"})," and ",(0,i.jsx)(n.a,{href:"/rx-pipeline.html",children:"RxPipeline"}),". On each write to a given source ",(0,i.jsx)(n.a,{href:"/rx-collection.html",children:"RxCollection"}),", an indexer is running to map the written document data into a fulltext search index.\nThe index can then be queried efficiently with complex fulltext search operations."]}),"\n",(0,i.jsx)(n.h2,{id:"benefits-of-using-a-local-fulltext-search",children:"Benefits of using a local fulltext search"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsx)(n.li,{children:"Efficient Search and Indexing"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["The plugin utilizes the ",(0,i.jsx)(n.a,{href:"https://github.com/nextapps-de/flexsearch",children:"FlexSearch library"}),", known for its speed and memory efficiency. This ensures that search operations are performed quickly, even with large datasets. The search engine can handle multi-field queries, partial matching, and complex search operations, providing users with highly relevant results."]}),"\n",(0,i.jsxs)(n.ol,{start:"2",children:["\n",(0,i.jsx)(n.li,{children:"Local Data Indexing"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["With the plugin, all search operations are performed on the local data stored within the RxDB collections. This means that users can execute fulltext search queries without the need for an external server or database, which is especially beneficial for offline-first applications. The local indexing ensures that search queries are executed quickly, reducing the latency typically associated with remote database queries. Also when used in multiple browser tabs, it is ensured that through ",(0,i.jsx)(n.a,{href:"/leader-election.html",children:"Leader Election"}),", only exactly one tabs is doing the work of indexing without having an overhead in the other browser tabs."]}),"\n",(0,i.jsxs)(n.ol,{start:"3",children:["\n",(0,i.jsx)(n.li,{children:"Real-time Indexing"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["The plugin integrates seamlessly with RxDB's reactive nature. Every time a document is written to an ",(0,i.jsx)(n.a,{href:"/rx-collection.html",children:"RxCollection"}),", an indexer updates the fulltext search index in real-time. This ensures that search results are always up-to-date, reflecting the most current state of the data without requiring manual reindexing."]}),"\n",(0,i.jsxs)(n.ol,{start:"4",children:["\n",(0,i.jsx)(n.li,{children:"Persistent indexing"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["The fulltext search index is efficiently persisted within the ",(0,i.jsx)(n.a,{href:"/rx-collection.html",children:"RxCollection"}),", ensuring that the index remains intact across app restarts. When documents are added or updated in the collection, the index is incrementally updated in real-time, meaning only the changes are processed rather than reindexing the entire dataset. This incremental approach not only optimizes performance but also ensures that subsequent app launches are quick, as there's no need to reindex all the data from scratch, making the search feature both reliable and fast from the moment the app starts. When using an ",(0,i.jsx)(n.a,{href:"/encryption.html",children:"encrypted storage"})," the index itself and incremental updates to it are stored fully encrypted and are only decrypted in-memory."]}),"\n",(0,i.jsxs)(n.ol,{start:"5",children:["\n",(0,i.jsx)(n.li,{children:"Complex Query Support"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["The FlexSearch-based plugin allows for ",(0,i.jsx)(n.a,{href:"https://github.com/nextapps-de/flexsearch?tab=readme-ov-file#index.search",children:"sophisticated search queries"}),", including multi-term and contextual searches. Users can perform complex searches that go beyond simple keyword matching, enabling more advanced use cases like searching for documents with specific phrases, relevance-based sorting, or even phonetic matching."]}),"\n",(0,i.jsxs)(n.ol,{start:"6",children:["\n",(0,i.jsx)(n.li,{children:"Offline-First Support and Privacy"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["As RxDB is designed with ",(0,i.jsx)(n.a,{href:"/offline-first.html",children:"offline-first applications"})," in mind, the fulltext search plugin supports this paradigm by ensuring that all search operations can be performed offline. This is crucial for applications that need to function in environments with intermittent or no internet connectivity, offering users a consistent and reliable search experience with ",(0,i.jsx)(n.a,{href:"/articles/zero-latency-local-first.html",children:"zero latency"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"using-the-rxdb-fulltext-search",children:"Using the RxDB Fulltext Search"}),"\n",(0,i.jsxs)(n.p,{children:["The flexsearch search is a ",(0,i.jsx)(n.a,{href:"/premium/",children:"RxDB Premium Package \ud83d\udc51"})," which must be purchased and imported from the ",(0,i.jsx)(n.code,{children:"rxdb-premium"})," npm package."]}),"\n",(0,i.jsxs)(n.p,{children:["Step 1: Add the ",(0,i.jsx)(n.code,{children:"RxDBFlexSearchPlugin"})," to RxDB."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBFlexSearchPlugin } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/flexsearch'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBFlexSearchPlugin);"})]})]})})}),"\n",(0,i.jsxs)(n.p,{children:["Step 2: Create a ",(0,i.jsx)(n.code,{children:"RxFulltextSearch"})," instance on top of a collection with the ",(0,i.jsx)(n.code,{children:"addFulltextSearch()"})," function."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { addFulltextSearch } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/flexsearch'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" flexSearch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" addFulltextSearch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // unique identifier. Used to store metadata and continue indexing on restarts/reloads."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-search'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // The source collection on whose documents the search is based on"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Transforms the document data to a given searchable string."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This can be done by returning a single string property of the document"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * or even by concatenating and transforming multiple fields like:"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * doc => doc.firstName + ' ' + doc.lastName"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" docToString"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" doc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".firstName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (Optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Amount of documents to index at once."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * See https://rxdb.info/rx-pipeline.html"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" number;"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (Optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * lazy: Initialize the in memory fulltext index at the first search query."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * instant: Directly initialize so that the index is already there on the first query."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Default: 'instant'"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" initialization: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'instant'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * (Optional)"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" https://github.com/nextapps-de/flexsearch#index-options"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" indexOptions"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.p,{children:"Step 3: Run a search operation:"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:'// find all documents whose searchstring contains "foobar"'})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" foundDocuments"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" flexSearch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can also use search options as second parameter"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"@link"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" https://github.com/nextapps-de/flexsearch#search-options"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" foundDocuments"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" flexSearch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { limit"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]})]})})})]})}function d(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8aa53ed7.941200b7.js b/docs/assets/js/8aa53ed7.941200b7.js
deleted file mode 100644
index 4fef35d3629..00000000000
--- a/docs/assets/js/8aa53ed7.941200b7.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6723],{7841(e,a,n){n.r(a),n.d(a,{assets:()=>c,contentTitle:()=>l,default:()=>p,frontMatter:()=>o,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"articles/angular-database","title":"RxDB as a Database in an Angular Application","description":"Level up your Angular projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.","source":"@site/docs/articles/angular-database.md","sourceDirName":"articles","slug":"/articles/angular-database.html","permalink":"/articles/angular-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB as a Database in an Angular Application","slug":"angular-database.html","description":"Level up your Angular projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser."},"sidebar":"tutorialSidebar","previous":{"title":"React Native Database - Sync & Store Like a Pro","permalink":"/react-native-database.html"},"next":{"title":"Browser Storage - RxDB as a Database for Browsers","permalink":"/articles/browser-storage.html"}}');var i=n(4848),r=n(8453),t=n(2271);const o={title:"RxDB as a Database in an Angular Application",slug:"angular-database.html",description:"Level up your Angular projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser."},l="RxDB as a Database in an Angular Application",c={},d=[{value:"Angular Web Applications",id:"angular-web-applications",level:2},{value:"Importance of Databases in Angular Applications",id:"importance-of-databases-in-angular-applications",level:2},{value:"Introducing RxDB as a Database Solution",id:"introducing-rxdb-as-a-database-solution",level:2},{value:"Getting Started with RxDB",id:"getting-started-with-rxdb",level:2},{value:"What is RxDB?",id:"what-is-rxdb",level:3},{value:"Reactive Data Handling",id:"reactive-data-handling",level:3},{value:"Offline-First Approach",id:"offline-first-approach",level:3},{value:"Data Replication",id:"data-replication",level:3},{value:"Observable Queries",id:"observable-queries",level:3},{value:"Multi-Tab Support",id:"multi-tab-support",level:3},{value:"RxDB vs. Other Angular Database Options",id:"rxdb-vs-other-angular-database-options",level:3},{value:"Using RxDB in an Angular Application",id:"using-rxdb-in-an-angular-application",level:2},{value:"Installing RxDB in an Angular App",id:"installing-rxdb-in-an-angular-app",level:3},{value:"Patch Change Detection with zone.js",id:"patch-change-detection-with-zonejs",level:3},{value:"Use the Angular async pipe to observe an RxDB Query",id:"use-the-angular-async-pipe-to-observe-an-rxdb-query",level:3},{value:"Different RxStorage layers for RxDB",id:"different-rxstorage-layers-for-rxdb",level:3},{value:"Synchronizing Data with RxDB between Clients and Servers",id:"synchronizing-data-with-rxdb-between-clients-and-servers",level:2},{value:"Offline-First Approach",id:"offline-first-approach-1",level:3},{value:"Conflict Resolution",id:"conflict-resolution",level:3},{value:"Bidirectional Synchronization",id:"bidirectional-synchronization",level:3},{value:"Real-Time Updates",id:"real-time-updates",level:3},{value:"Advanced RxDB Features and Techniques",id:"advanced-rxdb-features-and-techniques",level:2},{value:"Indexing and Performance Optimization",id:"indexing-and-performance-optimization",level:3},{value:"Encryption of Local Data",id:"encryption-of-local-data",level:3},{value:"Change Streams and Event Handling",id:"change-streams-and-event-handling",level:3},{value:"JSON Key Compression",id:"json-key-compression",level:3},{value:"Best Practices for Using RxDB in Angular Applications",id:"best-practices-for-using-rxdb-in-angular-applications",level:2},{value:"Use Async Pipe for Subscriptions so you do not have to unsubscribe",id:"use-async-pipe-for-subscriptions-so-you-do-not-have-to-unsubscribe",level:3},{value:"Use custom reactivity to have signals instead of rxjs observables",id:"use-custom-reactivity-to-have-signals-instead-of-rxjs-observables",level:3},{value:"Use Angular Services for Database creation",id:"use-angular-services-for-database-creation",level:3},{value:"Efficient Data Handling",id:"efficient-data-handling",level:3},{value:"Data Synchronization Strategies",id:"data-synchronization-strategies",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Follow Up",id:"follow-up",level:2}];function h(e){const a={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(a.header,{children:(0,i.jsx)(a.h1,{id:"rxdb-as-a-database-in-an-angular-application",children:"RxDB as a Database in an Angular Application"})}),"\n",(0,i.jsxs)(a.p,{children:["In modern web development, Angular has emerged as a popular framework for building robust and scalable applications. As Angular applications often require persistent ",(0,i.jsx)(a.a,{href:"/articles/browser-storage.html",children:"storage"})," and efficient data handling, choosing the right database solution is crucial. One such solution is ",(0,i.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"}),", a reactive JavaScript database for the browser, node.js, and ",(0,i.jsx)(a.a,{href:"/articles/mobile-database.html",children:"mobile devices"}),". In this article, we will explore the integration of RxDB into an Angular application and examine its various features and techniques."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Angular Database",width:"220"})})}),"\n",(0,i.jsx)(a.h2,{id:"angular-web-applications",children:"Angular Web Applications"}),"\n",(0,i.jsx)(a.p,{children:"Angular is a powerful JavaScript framework developed and maintained by Google. It enables developers to build single-page applications (SPAs) with a modular and component-based approach. Angular provides a comprehensive set of tools and features for creating dynamic and responsive web applications."}),"\n",(0,i.jsx)(a.h2,{id:"importance-of-databases-in-angular-applications",children:"Importance of Databases in Angular Applications"}),"\n",(0,i.jsx)(a.p,{children:"Databases play a vital role in Angular applications by providing a structured and efficient way to store, retrieve, and manage data. Whether it's handling user authentication, caching data, or persisting application state, a robust database solution is essential for ensuring optimal performance and user experience."}),"\n",(0,i.jsx)(a.h2,{id:"introducing-rxdb-as-a-database-solution",children:"Introducing RxDB as a Database Solution"}),"\n",(0,i.jsxs)(a.p,{children:["RxDB stands for Reactive Database and is built on the principles of reactive programming. It combines the best features of ",(0,i.jsx)(a.a,{href:"/articles/in-memory-nosql-database.html",children:"NoSQL databases"})," with the power of reactive programming to provide a scalable and efficient database solution. RxDB offers seamless integration with Angular applications and brings several unique features that make it an attractive choice for developers."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)(t.N,{videoId:"qHWrooWyCYg",title:"This solved a problem I've had in Angular for years",duration:"3:45"})}),"\n",(0,i.jsx)(a.h2,{id:"getting-started-with-rxdb",children:"Getting Started with RxDB"}),"\n",(0,i.jsx)(a.p,{children:"To begin our journey with RxDB, let's understand its key concepts and features."}),"\n",(0,i.jsx)(a.h3,{id:"what-is-rxdb",children:"What is RxDB?"}),"\n",(0,i.jsxs)(a.p,{children:[(0,i.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"})," is a client-side database that follows the principles of reactive programming. It is built on top of IndexedDB, the ",(0,i.jsx)(a.a,{href:"/articles/browser-database.html",children:"native browser database"}),", and leverages the RxJS library for reactive data handling. RxDB provides a simple and intuitive API for managing data and offers features like data replication, multi-tab support, and efficient query handling."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Angular Database",width:"220"})})}),"\n",(0,i.jsx)(a.h3,{id:"reactive-data-handling",children:"Reactive Data Handling"}),"\n",(0,i.jsx)(a.p,{children:"At the core of RxDB is the concept of reactive data handling. RxDB leverages observables and reactive streams to enable real-time updates and data synchronization. With RxDB, you can easily subscribe to data changes and react to them in a reactive and efficient manner."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/animations/realtime.gif",alt:"realtime ui updates",width:"700"})}),"\n",(0,i.jsx)(a.h3,{id:"offline-first-approach",children:"Offline-First Approach"}),"\n",(0,i.jsx)(a.p,{children:"One of the standout features of RxDB is its offline-first approach. It allows you to build applications that can work seamlessly in offline scenarios. RxDB stores data locally and automatically synchronizes changes with the server when the network becomes available. This capability is particularly useful for applications that need to function in low-connectivity or unreliable network environments."}),"\n",(0,i.jsx)(a.h3,{id:"data-replication",children:"Data Replication"}),"\n",(0,i.jsx)(a.p,{children:"RxDB provides built-in support for data replication between clients and servers. This means you can synchronize data across multiple devices or instances of your application effortlessly. RxDB handles conflict resolution and ensures that data remains consistent across all connected clients."}),"\n",(0,i.jsx)(a.h3,{id:"observable-queries",children:"Observable Queries"}),"\n",(0,i.jsx)(a.p,{children:"RxDB offers a powerful querying mechanism with support for observable queries. This allows you to create dynamic queries that automatically update when the underlying data changes. By leveraging RxDB's observable queries, you can build reactive UI components that respond to data changes in real-time."}),"\n",(0,i.jsx)(a.h3,{id:"multi-tab-support",children:"Multi-Tab Support"}),"\n",(0,i.jsx)(a.p,{children:"RxDB provides out-of-the-box support for multi-tab scenarios. This means that if your Angular application is running in multiple browser tabs, RxDB automatically keeps the data in sync across all tabs. It ensures that changes made in one tab are immediately reflected in others, providing a seamless user experience."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/multiwindow.gif",alt:"multi tab support",width:"450"})}),"\n",(0,i.jsx)(a.h3,{id:"rxdb-vs-other-angular-database-options",children:"RxDB vs. Other Angular Database Options"}),"\n",(0,i.jsx)(a.p,{children:"While there are other database options available for Angular applications, RxDB stands out with its reactive programming model, offline-first approach, and built-in synchronization capabilities. Unlike traditional SQL databases, RxDB's NoSQL-like structure and observables-based API make it well-suited for real-time applications and complex data scenarios."}),"\n",(0,i.jsx)(a.h2,{id:"using-rxdb-in-an-angular-application",children:"Using RxDB in an Angular Application"}),"\n",(0,i.jsx)(a.p,{children:"Now that we have a good understanding of RxDB and its features, let's explore how to integrate it into an Angular application."}),"\n",(0,i.jsx)(a.h3,{id:"installing-rxdb-in-an-angular-app",children:"Installing RxDB in an Angular App"}),"\n",(0,i.jsx)(a.p,{children:"To use RxDB in an Angular application, we first need to install the necessary dependencies. You can install RxDB using npm or yarn by running the following command:"}),"\n",(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(a.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string)"},children:" --save"})]})})})}),"\n",(0,i.jsx)(a.p,{children:"Once installed, you can import RxDB into your Angular application and start using its API to create and manage databases."}),"\n",(0,i.jsx)(a.h3,{id:"patch-change-detection-with-zonejs",children:"Patch Change Detection with zone.js"}),"\n",(0,i.jsx)(a.p,{children:"Angular uses change detection to detect and update UI elements when data changes. However, RxDB's data handling is based on observables, which can sometimes bypass Angular's change detection mechanism. To ensure that changes made in RxDB are detected by Angular, we need to patch the change detection mechanism using zone.js. Zone.js is a library that intercepts and tracks asynchronous operations, including observables. By patching zone.js, we can make sure that Angular is aware of changes happening in RxDB."}),"\n",(0,i.jsxs)(a.admonition,{type:"warning",children:[(0,i.jsxs)(a.p,{children:["RxDB creates rxjs observables outside of angulars zone\nSo you have to import the rxjs patch to ensure the ",(0,i.jsx)(a.a,{href:"https://angular.io/guide/change-detection",children:"angular change detection"})," works correctly.\n",(0,i.jsx)(a.a,{href:"https://www.bennadel.com/blog/3448-binding-rxjs-observable-sources-outside-of-the-ngzone-in-angular-6-0-2.htm",children:"link"})]}),(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(a.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"//> app.component.ts"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'zone.js/plugins/zone-patch-rxjs'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})})]}),"\n",(0,i.jsx)(a.h3,{id:"use-the-angular-async-pipe-to-observe-an-rxdb-query",children:"Use the Angular async pipe to observe an RxDB Query"}),"\n",(0,i.jsx)(a.p,{children:"Angular provides the async pipe, which is a convenient way to subscribe to observables and handle the subscription lifecycle automatically. When working with RxDB, you can use the async pipe to observe an RxDB query and bind the results directly to your Angular template. This ensures that the UI stays in sync with the data changes emitted by the RxDB query."}),"\n",(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(a.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" constructor"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" private dbService: DatabaseService"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" private dialog: MatDialog"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ) {"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".heroes$ "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".dbService"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ."}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".hero "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// collection"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" .find"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"({ "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// query"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" [{ name"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" .$;"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" }"})})]})})}),"\n",(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"html","data-theme":"css-variables",children:(0,i.jsxs)(a.code,{"data-language":"html","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" *ngFor"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"let hero of heroes$ | async as heroes;"'}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:">{{hero.name}}"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:">"})]})]})})}),"\n",(0,i.jsx)(a.h3,{id:"different-rxstorage-layers-for-rxdb",children:"Different RxStorage layers for RxDB"}),"\n",(0,i.jsx)(a.p,{children:"RxDB supports multiple storage layers for persisting data. Some of the available storage options include:"}),"\n",(0,i.jsxs)(a.ul,{children:["\n",(0,i.jsxs)(a.li,{children:[(0,i.jsx)(a.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"}),": Uses the ",(0,i.jsx)(a.a,{href:"/articles/localstorage.html",children:"LocalStorage API"})," without any third party plugins."]}),"\n",(0,i.jsxs)(a.li,{children:[(0,i.jsx)(a.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),": RxDB directly supports IndexedDB as a storage layer. IndexedDB is a low-level browser database that offers good performance and reliability."]}),"\n",(0,i.jsxs)(a.li,{children:[(0,i.jsx)(a.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"}),": The OPFS ",(0,i.jsx)(a.a,{href:"/rx-storage.html",children:"RxStorage"})," for RxDB is built on top of the ",(0,i.jsx)(a.a,{href:"https://webkit.org/blog/12257/the-file-system-access-api-with-origin-private-file-system/",children:"File System Access API"})," which is available in ",(0,i.jsx)(a.a,{href:"https://caniuse.com/native-filesystem-api",children:"all modern browsers"}),". It provides an API to access a sandboxed private file system to persistently store and retrieve data.\nCompared to other persistent storage options in the browser (like ",(0,i.jsx)(a.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"}),"), the OPFS API has a ",(0,i.jsx)(a.strong,{children:"way better performance"}),"."]}),"\n",(0,i.jsxs)(a.li,{children:[(0,i.jsx)(a.a,{href:"/rx-storage-memory.html",children:"Memory RxStorage"}),": In addition to persistent storage options, RxDB also provides a memory-based storage layer. This is useful for testing or scenarios where you don't need long-term data persistence.\nYou can choose the storage layer that best suits your application's requirements and configure RxDB accordingly."]}),"\n"]}),"\n",(0,i.jsx)(a.h2,{id:"synchronizing-data-with-rxdb-between-clients-and-servers",children:"Synchronizing Data with RxDB between Clients and Servers"}),"\n",(0,i.jsx)(a.p,{children:"Data replication between an Angular application and a server is a common requirement. RxDB simplifies this process and provides built-in support for data synchronization. Let's explore how to replicate data between an Angular application and a server using RxDB."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/database-replication.png",alt:"database replication",width:"200"})}),"\n",(0,i.jsx)(a.h3,{id:"offline-first-approach-1",children:"Offline-First Approach"}),"\n",(0,i.jsxs)(a.p,{children:["One of the key strengths of RxDB is its ",(0,i.jsx)(a.a,{href:"/offline-first.html",children:"offline-first approach"}),". It allows Angular applications to function seamlessly even in offline scenarios. RxDB stores data locally and automatically synchronizes changes with the server when the network becomes available. This capability is particularly useful for applications that need to operate in low-connectivity or unreliable network environments."]}),"\n",(0,i.jsx)(a.h3,{id:"conflict-resolution",children:"Conflict Resolution"}),"\n",(0,i.jsx)(a.p,{children:"In a distributed system, conflicts can arise when multiple clients modify the same data simultaneously. RxDB offers conflict resolution mechanisms to handle such scenarios. You can define conflict resolution strategies based on your application's requirements. RxDB provides hooks and events to detect conflicts and resolve them in a consistent manner."}),"\n",(0,i.jsx)(a.h3,{id:"bidirectional-synchronization",children:"Bidirectional Synchronization"}),"\n",(0,i.jsx)(a.p,{children:"RxDB supports bidirectional data synchronization, allowing updates from both the client and server to be replicated seamlessly. This ensures that data remains consistent across all connected clients and the server. RxDB handles conflicts and resolves them based on the defined conflict resolution strategies."}),"\n",(0,i.jsx)(a.h3,{id:"real-time-updates",children:"Real-Time Updates"}),"\n",(0,i.jsx)(a.p,{children:"RxDB provides real-time updates by leveraging reactive programming principles. Changes made to the data are automatically propagated to all connected clients in real-time. Angular applications can subscribe to these updates and update the user interface accordingly. This real-time capability enables collaborative features and enhances the overall user experience."}),"\n",(0,i.jsx)(a.h2,{id:"advanced-rxdb-features-and-techniques",children:"Advanced RxDB Features and Techniques"}),"\n",(0,i.jsx)(a.p,{children:"RxDB offers several advanced features and techniques that can further enhance your Angular application."}),"\n",(0,i.jsx)(a.h3,{id:"indexing-and-performance-optimization",children:"Indexing and Performance Optimization"}),"\n",(0,i.jsx)(a.p,{children:"To improve query performance, RxDB allows you to define indexes on specific fields of your documents. Indexing enables faster data retrieval and query execution, especially when working with large datasets. By strategically creating indexes, you can optimize the performance of your Angular application."}),"\n",(0,i.jsx)(a.h3,{id:"encryption-of-local-data",children:"Encryption of Local Data"}),"\n",(0,i.jsxs)(a.p,{children:["RxDB provides built-in support for ",(0,i.jsx)(a.a,{href:"/encryption.html",children:"encrypting"})," local data using the Web Crypto API. With encryption, you can protect sensitive data stored in the client-side database. RxDB transparently encrypts the data, ensuring that it remains secure even if the underlying storage is compromised."]}),"\n",(0,i.jsx)(a.h3,{id:"change-streams-and-event-handling",children:"Change Streams and Event Handling"}),"\n",(0,i.jsx)(a.p,{children:"RxDB exposes change streams, which allow you to listen for data changes at a database or collection level. By subscribing to change streams, you can react to data modifications and perform specific actions, such as updating the UI or triggering notifications. Change streams enable real-time event handling in your Angular application."}),"\n",(0,i.jsx)(a.h3,{id:"json-key-compression",children:"JSON Key Compression"}),"\n",(0,i.jsxs)(a.p,{children:["To reduce the storage footprint and improve performance, RxDB supports ",(0,i.jsx)(a.a,{href:"/key-compression.html",children:"JSON key compression"}),". With key compression, RxDB replaces long keys with shorter aliases, reducing the overall storage size. This optimization is particularly useful when working with large datasets or frequently updating data."]}),"\n",(0,i.jsx)(a.h2,{id:"best-practices-for-using-rxdb-in-angular-applications",children:"Best Practices for Using RxDB in Angular Applications"}),"\n",(0,i.jsx)(a.p,{children:"To make the most of RxDB in your Angular application, consider the following best practices:"}),"\n",(0,i.jsx)(a.h3,{id:"use-async-pipe-for-subscriptions-so-you-do-not-have-to-unsubscribe",children:"Use Async Pipe for Subscriptions so you do not have to unsubscribe"}),"\n",(0,i.jsxs)(a.p,{children:["Angular's ",(0,i.jsx)(a.code,{children:"async"})," pipe is a powerful tool for handling observables in templates. By using the async pipe, you can avoid the need to manually subscribe and unsubscribe from RxDB observables. Angular takes care of the subscription lifecycle, ensuring that resources are released when they are no longer needed. Instead of manually subscribing to Observables, you should always prefer the ",(0,i.jsx)(a.code,{children:"async"})," pipe."]}),"\n",(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(a.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// WRONG:"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" amount;"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".dbService"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ."}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".hero"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" .find"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" [{ name"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ."}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(docs "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" amount "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" docs"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".forEach"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(d "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" amount "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" d"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".points);"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// RIGHT:"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"this"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".amount$ "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".dbService"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ."}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".hero"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" .find"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {}"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" [{ name"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ."}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".pipe"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" map"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(docs "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" let"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" amount "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" docs"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".forEach"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(d "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" amount "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" d"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".points);"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" amount;"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" );"})})]})})}),"\n",(0,i.jsx)(a.h3,{id:"use-custom-reactivity-to-have-signals-instead-of-rxjs-observables",children:"Use custom reactivity to have signals instead of rxjs observables"}),"\n",(0,i.jsxs)(a.p,{children:["RxDB supports adding custom reactivity factories that allow you to get angular signals out of the database instead of rxjs observables. ",(0,i.jsx)(a.a,{href:"/reactivity.html",children:"read more"}),"."]}),"\n",(0,i.jsx)(a.h3,{id:"use-angular-services-for-database-creation",children:"Use Angular Services for Database creation"}),"\n",(0,i.jsx)(a.p,{children:"To ensure proper separation of concerns and maintain a clean codebase, it is recommended to create an Angular service responsible for managing the RxDB database instance. This service can handle database creation, initialization, and provide methods for interacting with the database throughout your application."}),"\n",(0,i.jsx)(a.h3,{id:"efficient-data-handling",children:"Efficient Data Handling"}),"\n",(0,i.jsx)(a.p,{children:"RxDB provides various mechanisms for efficient data handling, such as batching updates, debouncing, and throttling. Leveraging these techniques can help optimize performance and reduce unnecessary UI updates. Consider the specific data handling requirements of your application and choose the appropriate strategies provided by RxDB."}),"\n",(0,i.jsx)(a.h3,{id:"data-synchronization-strategies",children:"Data Synchronization Strategies"}),"\n",(0,i.jsx)(a.p,{children:"When working with data synchronization between clients and servers, it's important to consider strategies for conflict resolution and handling network failures. RxDB provides plugins and hooks that allow you to customize the replication behavior and implement specific synchronization strategies tailored to your application's needs."}),"\n",(0,i.jsx)(a.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,i.jsx)(a.p,{children:"RxDB is a powerful database solution for Angular applications, offering reactive data handling, offline-first capabilities, and seamless data synchronization. By integrating RxDB into your Angular application, you can build responsive and scalable web applications that provide a rich user experience. Whether you're building real-time collaborative apps, progressive web applications, or offline-capable applications, RxDB's features and techniques make it a valuable addition to your Angular development toolkit."}),"\n",(0,i.jsx)(a.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsx)(a.p,{children:"To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:"}),"\n",(0,i.jsxs)(a.ul,{children:["\n",(0,i.jsxs)(a.li,{children:[(0,i.jsx)(a.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB GitHub Repository"}),": Visit the official GitHub repository of RxDB to access the source code, documentation, and community support."]}),"\n",(0,i.jsxs)(a.li,{children:[(0,i.jsx)(a.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),": Get started quickly with RxDB by following the provided quickstart guide, which provides step-by-step instructions for setting up and using RxDB in your projects."]}),"\n",(0,i.jsx)(a.li,{children:(0,i.jsx)(a.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/angular",children:"RxDB Angular Example at GitHub"})}),"\n"]})]})}function p(e={}){const{wrapper:a}={...(0,r.R)(),...e.components};return a?(0,i.jsx)(a,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,a,n){n.d(a,{R:()=>t,x:()=>o});var s=n(6540);const i={},r=s.createContext(i);function t(e){const a=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(a):{...a,...e}},[a,e])}function o(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:t(e.components),s.createElement(r.Provider,{value:a},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8b0a0922.114db760.js b/docs/assets/js/8b0a0922.114db760.js
deleted file mode 100644
index e72282fbaad..00000000000
--- a/docs/assets/js/8b0a0922.114db760.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4557],{8453(e,n,s){s.d(n,{R:()=>o,x:()=>a});var r=s(6540);const i={},t=r.createContext(i);function o(e){const n=r.useContext(t);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),r.createElement(t.Provider,{value:n},e.children)}},9463(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"slow-indexeddb","title":"Solving IndexedDB Slowness for Seamless Apps","description":"Struggling with IndexedDB performance? Discover hidden bottlenecks, advanced tuning techniques, and next-gen storage like the File System Access API.","source":"@site/docs/slow-indexeddb.md","sourceDirName":".","slug":"/slow-indexeddb.html","permalink":"/slow-indexeddb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Solving IndexedDB Slowness for Seamless Apps","slug":"slow-indexeddb.html","description":"Struggling with IndexedDB performance? Discover hidden bottlenecks, advanced tuning techniques, and next-gen storage like the File System Access API."},"sidebar":"tutorialSidebar","previous":{"title":"NoSQL Performance Tips","permalink":"/nosql-performance-tips.html"},"next":{"title":"LocalStorage vs. IndexedDB vs. Cookies vs. OPFS vs. WASM-SQLite","permalink":"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html"}}');var i=s(4848),t=s(8453);const o={title:"Solving IndexedDB Slowness for Seamless Apps",slug:"slow-indexeddb.html",description:"Struggling with IndexedDB performance? Discover hidden bottlenecks, advanced tuning techniques, and next-gen storage like the File System Access API."},a="Why IndexedDB is slow and what to use instead",l={},d=[{value:"Batched Cursor",id:"batched-cursor",level:2},{value:"IndexedDB Sharding",id:"indexeddb-sharding",level:2},{value:"Custom Indexes",id:"custom-indexes",level:2},{value:"Relaxed durability",id:"relaxed-durability",level:2},{value:"Explicit transaction commits",id:"explicit-transaction-commits",level:2},{value:"In-Memory on top of IndexedDB",id:"in-memory-on-top-of-indexeddb",level:2},{value:"In-Memory: Persistence",id:"in-memory-persistence",level:3},{value:"In-Memory: Multi Tab Support",id:"in-memory-multi-tab-support",level:3},{value:"Further read",id:"further-read",level:2}];function c(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"why-indexeddb-is-slow-and-what-to-use-instead",children:"Why IndexedDB is slow and what to use instead"})}),"\n",(0,i.jsxs)(n.p,{children:["So you have a JavaScript web application that needs to store data at the client side, either to make it ",(0,i.jsx)(n.a,{href:"/offline-first.html",children:"offline usable"}),", just for caching purposes or for other reasons."]}),"\n",(0,i.jsxs)(n.p,{children:["For ",(0,i.jsx)(n.a,{href:"/articles/browser-database.html",children:"in-browser data storage"}),", you have some options:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Cookies"})," are sent with each HTTP request, so you cannot store more than a few strings in them."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"WebSQL"})," ",(0,i.jsx)(n.a,{href:"https://hacks.mozilla.org/2010/06/beyond-html5-database-apis-and-the-road-to-indexeddb/",children:"is deprecated"})," because it never was a real standard and turning it into a standard would have been too difficult."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.a,{href:"/articles/localstorage.html",children:"LocalStorage"})," is a synchronous API over asynchronous IO-access. Storing and reading data can fully block the JavaScript process so you cannot use LocalStorage for more than few simple key-value pairs."]}),"\n",(0,i.jsxs)(n.li,{children:["The ",(0,i.jsx)(n.strong,{children:"FileSystem API"})," could be used to store plain binary files, but it is ",(0,i.jsx)(n.a,{href:"https://caniuse.com/filesystem",children:"only supported in chrome"})," for now."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"IndexedDB"})," is an indexed key-object database. It can store json data and iterate over its indexes. It is ",(0,i.jsx)(n.a,{href:"https://caniuse.com/indexeddb",children:"widely supported"})," and stable."]}),"\n"]}),"\n",(0,i.jsxs)(n.admonition,{title:"UPDATE April 2023",type:"note",children:[(0,i.jsxs)(n.p,{children:["Since beginning of 2023, all modern browsers ship the ",(0,i.jsx)(n.strong,{children:"File System Access API"})," which allows to persistently store data in the browser with a way better performance. For ",(0,i.jsx)(n.a,{href:"https://rxdb.info/",children:"RxDB"})," you can use the ",(0,i.jsx)(n.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"})," to get about 4x performance improvement compared to IndexedDB."]}),(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"./files/logo/rxdb_javascript_database.svg",alt:"IndexedDB Database",width:"250"})})})]}),"\n",(0,i.jsxs)(n.p,{children:["It becomes clear that the only way to go is IndexedDB. You start developing your app and everything goes fine.\nBut as soon as your app gets bigger, more complex or just handles more data, you might notice something. ",(0,i.jsx)(n.strong,{children:"IndexedDB is slow"}),". Not slow like a database on a cheap server, ",(0,i.jsx)(n.strong,{children:"even slower"}),"! Inserting a few hundred documents can take up several seconds. Time which can be critical for a fast page load. Even sending data over the internet to the backend can be faster than storing it inside of an IndexedDB database."]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"Transactions vs Throughput"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["So before we start complaining, lets analyze what exactly is slow. When you run tests on Nolans ",(0,i.jsx)(n.a,{href:"http://nolanlawson.github.io/database-comparison/",children:"Browser Database Comparison"})," you can see that inserting 1k documents into IndexedDB takes about 80 milliseconds, 0.08ms per document. This is not really slow. It is quite fast and it is very unlikely that you want to store that many document at the same time at the client side. But the key point here is that all these documents get written in a ",(0,i.jsx)(n.code,{children:"single transaction"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["I forked the comparison tool ",(0,i.jsx)(n.a,{href:"https://pubkey.github.io/client-side-databases/database-comparison/index.html",children:"here"})," and changed it to use one transaction per document write. And there we have it. Inserting 1k documents with one transaction per write, takes about 2 seconds. Interestingly if we increase the document size to be 100x bigger, it still takes about the same time to store them. This makes clear that the limiting factor to IndexedDB performance is the transaction handling, not the data throughput."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/indexeddb-transaction-throughput.png",alt:"IndexedDB transaction throughput",width:"700"})}),"\n",(0,i.jsxs)(n.p,{children:["To fix your IndexedDB performance problems you have to make sure to use as less data transfers/transactions as possible.\nSometimes this is easy, as instead of iterating over a documents list and calling single inserts, with RxDB you could use the ",(0,i.jsx)(n.a,{href:"https://rxdb.info/rx-collection.html#bulkinsert",children:"bulk methods"})," to store many document at once.\nBut most of the time is not so easy. Your user clicks around, data gets replicated from the backend, another browser tab writes data. All these things can happen at random time and you cannot crunch all that data in a single transaction."]}),"\n",(0,i.jsxs)(n.p,{children:["Another solution is to just not care about performance at all. In a few releases the browser vendors will have optimized IndexedDB and everything is fast again. Well, IndexedDB was slow ",(0,i.jsx)(n.a,{href:"https://www.researchgate.net/publication/281065948_Performance_Testing_and_Comparison_of_Client_Side_Databases_Versus_Server_Side",children:"in 2013"})," and it is still slow today. If this trend continues, it will still be slow in a few years from now. Waiting is not an option. The chromium devs made ",(0,i.jsx)(n.a,{href:"https://bugs.chromium.org/p/chromium/issues/detail?id=1025456#c15",children:"a statement"})," to focus on optimizing read performance, not write performance."]}),"\n",(0,i.jsxs)(n.p,{children:["Switching to WebSQL (even if it is deprecated) is also not an option because, like ",(0,i.jsx)(n.a,{href:"https://pubkey.github.io/client-side-databases/database-comparison/index.html",children:"the comparison tool shows"}),", it has even slower transactions."]}),"\n",(0,i.jsxs)(n.p,{children:["So you need a way to ",(0,i.jsx)(n.strong,{children:"make IndexedDB faster"}),". In the following I lay out some performance optimizations than can be made to have faster reads and writes in IndexedDB."]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"HINT:"})," You can reproduce all performance tests ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/indexeddb-performance-tests",children:"in this repo"}),". In all tests we work on a dataset of 40000 ",(0,i.jsx)(n.code,{children:"human"})," documents with a random ",(0,i.jsx)(n.code,{children:"age"})," between ",(0,i.jsx)(n.code,{children:"1"})," and ",(0,i.jsx)(n.code,{children:"100"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"batched-cursor",children:"Batched Cursor"}),"\n",(0,i.jsxs)(n.p,{children:["With ",(0,i.jsx)(n.a,{href:"https://caniuse.com/indexeddb2",children:"IndexedDB 2.0"}),", new methods were introduced which can be utilized to improve performance. With the ",(0,i.jsx)(n.code,{children:"getAll()"})," method, a faster alternative to the old ",(0,i.jsx)(n.code,{children:"openCursor()"})," can be created which improves performance when reading data from the IndexedDB store."]}),"\n",(0,i.jsxs)(n.p,{children:["Lets say we want to query all user documents that have an ",(0,i.jsx)(n.code,{children:"age"})," greater than ",(0,i.jsx)(n.code,{children:"25"})," out of the store.\nTo implement a fast batched cursor that only needs calls to ",(0,i.jsx)(n.code,{children:"getAll()"})," and not to ",(0,i.jsx)(n.code,{children:"getAllKeys()"}),", we first need to create an ",(0,i.jsx)(n.code,{children:"age"})," index that contains the primary ",(0,i.jsx)(n.code,{children:"id"})," as last field."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myIndexedDBObjectStore"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".createIndex"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'age-index'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'age'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["This is required because the ",(0,i.jsx)(n.code,{children:"age"})," field is not unique, and we need a way to checkpoint the last returned batch so we can continue from there in the next call to ",(0,i.jsx)(n.code,{children:"getAll()"}),"."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" maxAge"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 25"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" result "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" [];"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" tx"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" IDBTransaction"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".transaction"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"([storeName]"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'readonly'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" TRANSACTION_SETTINGS"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" store"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" tx"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".objectStore"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(storeName);"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" index"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" store"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".index"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'age-index'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastDoc;"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" done "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Run the batched cursor until all results are retrieved"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * or the end of the index is reached."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"while"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (done "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"((res"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" rej) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" range"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" IDBKeyRange"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".bound"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If we have a previous document as checkpoint,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * we have to continue from it's age and id values."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastDoc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"?"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" lastDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".age "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" -"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Infinity"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastDoc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"?"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" lastDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" -"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Infinity"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" maxAge "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0.00000001"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" String"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".fromCharCode"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"65535"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" openCursorRequest"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" index"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".getAll"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(range"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize);"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" openCursorRequest"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onerror"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" err "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" rej"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(err);"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" openCursorRequest"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"onsuccess"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" e "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" subResult"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" TestDocument"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"[] "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" e"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"target"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".result;"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastDoc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" lastOfArray"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(subResult);"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"subResult"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ==="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" done "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"else"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" result "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".concat"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(subResult);"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" res"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(result);"})]})]})})}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/indexeddb-batched-cursor.png",alt:"IndexedDB batched cursor",width:"100%"})}),"\n",(0,i.jsxs)(n.p,{children:["As the performance test results show, using a batched cursor can give a huge improvement. Interestingly choosing a high batch size is important. When you known that all results of a given ",(0,i.jsx)(n.code,{children:"IDBKeyRange"})," are needed, you should not set a batch size at all and just directly query all documents via ",(0,i.jsx)(n.code,{children:"getAll()"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["RxDB uses batched cursors in the ",(0,i.jsx)(n.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"indexeddb-sharding",children:"IndexedDB Sharding"}),"\n",(0,i.jsxs)(n.p,{children:["Sharding is a technique, normally used in server side databases, where the database is partitioned horizontally. Instead of storing all documents at one table/collection, the documents are split into so called ",(0,i.jsx)(n.strong,{children:"shards"})," and each shard is stored on one table/collection. This is done in server side architectures to spread the load between multiple physical servers which ",(0,i.jsx)(n.strong,{children:"increases scalability"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["When you use IndexedDB in a browser, there is of course no way to split the load between the client and other servers. But you can still benefit from sharding. Partitioning the documents horizontally into ",(0,i.jsx)(n.strong,{children:"multiple IndexedDB stores"}),", has shown to have a big performance improvement in write- and read operations while only increasing initial pageload slightly."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/indexeddb-sharding-performance.png",alt:"IndexedDB sharding performance",width:"100%"})}),"\n",(0,i.jsxs)(n.p,{children:["As shown in the performance test results, sharding should always be done by ",(0,i.jsx)(n.code,{children:"IDBObjectStore"})," and not by database. Running a batched cursor over the whole dataset with 10 store shards in parallel is about ",(0,i.jsx)(n.strong,{children:"28% faster"})," then running it over a single store. Initialization time increases minimal from ",(0,i.jsx)(n.code,{children:"9"})," to ",(0,i.jsx)(n.code,{children:"17"})," milliseconds.\nGetting a quarter of the dataset by batched iterating over an index, is even ",(0,i.jsx)(n.strong,{children:"43%"})," faster with sharding then when a single store is queried."]}),"\n",(0,i.jsx)(n.p,{children:"As downside, getting 10k documents by their id is slower when it has to run over the shards.\nAlso it can be much effort to recombined the results from the different shards into the required query result. When a query without a limit is done, the sharding method might cause a data load huge overhead."}),"\n",(0,i.jsxs)(n.p,{children:["Sharding can be used with RxDB with the ",(0,i.jsx)(n.a,{href:"/rx-storage-sharding.html",children:"Sharding Plugin"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"custom-indexes",children:"Custom Indexes"}),"\n",(0,i.jsx)(n.p,{children:"Indexes improve the query performance of IndexedDB significant. Instead of fetching all data from the storage when you search for a subset of it, you can iterate over the index and stop iterating when all relevant data has been found."}),"\n",(0,i.jsxs)(n.p,{children:["For example to query for all user documents that have an ",(0,i.jsx)(n.code,{children:"age"})," greater than ",(0,i.jsx)(n.code,{children:"25"}),", you would create an ",(0,i.jsx)(n.code,{children:"age+id"})," index.\nTo be able to run a batched cursor over the index, we always need our primary key (",(0,i.jsx)(n.code,{children:"id"}),") as the last index field."]}),"\n",(0,i.jsxs)(n.p,{children:["Instead of doing this, you can use a ",(0,i.jsx)(n.code,{children:"custom index"})," which can improve the performance. The custom index runs over a helper field ",(0,i.jsx)(n.code,{children:"ageIdCustomIndex"})," which is added to each document on write. Our index now only contains a single ",(0,i.jsx)(n.code,{children:"string"})," field instead of two (age-",(0,i.jsx)(n.code,{children:"number"})," and id-",(0,i.jsx)(n.code,{children:"string"}),")."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// On document insert add the ageIdCustomIndex field."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" idMaxLength"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 20"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"; "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// must be known to craft a custom index"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"docData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".ageIdCustomIndex "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".age "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".padStart"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(idMaxLength"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ' '"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"store"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".put"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(docData);"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// ..."})})]})})}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// normal index"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myIndexedDBObjectStore"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".createIndex"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'age-index'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'age'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// custom index"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"myIndexedDBObjectStore"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".createIndex"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'age-index-custom'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'ageIdCustomIndex'"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsxs)(n.p,{children:["To iterate over the index, you also use a custom crafted keyrange, depending on the last batched cursor checkpoint. Therefore the ",(0,i.jsx)(n.code,{children:"maxLength"})," of ",(0,i.jsx)(n.code,{children:"id"})," must be known."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// keyrange for normal index"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" range"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" IDBKeyRange"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".bound"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"25"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ''"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Infinity"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Infinity"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// keyrange for custom index"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" range"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" IDBKeyRange"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".bound"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // combine both values to a single string"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 25"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ''"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".padStart"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(idMaxLength"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ' '"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Infinity"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/indexeddb-custom-index.png",alt:"IndexedDB custom index",width:"700"})}),"\n",(0,i.jsxs)(n.p,{children:["As shown, using a custom index can further improve the performance of running a batched cursor by about ",(0,i.jsx)(n.code,{children:"10%"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["Another big benefit of using custom indexes, is that you can also encode ",(0,i.jsx)(n.code,{children:"boolean"})," values in them, which ",(0,i.jsx)(n.a,{href:"https://github.com/w3c/IndexedDB/issues/76",children:"cannot be done"})," with normal IndexedDB indexes."]}),"\n",(0,i.jsxs)(n.p,{children:["RxDB uses custom indexes in the ",(0,i.jsx)(n.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"relaxed-durability",children:"Relaxed durability"}),"\n",(0,i.jsxs)(n.p,{children:["Chromium based browsers allow to set ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/IDBTransaction/durability",children:"durability"})," to ",(0,i.jsx)(n.code,{children:"relaxed"})," when creating an IndexedDB transaction. Which runs the transaction in a less secure durability mode, which can improve the performance."]}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsx)(n.p,{children:"The user agent may consider that the transaction has successfully committed as soon as all outstanding changes have been written to the operating system, without subsequent verification."}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["As shown ",(0,i.jsx)(n.a,{href:"https://nolanlawson.com/2021/08/22/speeding-up-indexeddb-reads-and-writes/",children:"here"}),", using the relaxed durability mode can improve performance slightly. The best performance improvement could be measured when many small transactions have to be run. Less, bigger transaction do not benefit that much."]}),"\n",(0,i.jsx)(n.h2,{id:"explicit-transaction-commits",children:"Explicit transaction commits"}),"\n",(0,i.jsxs)(n.p,{children:["By explicitly committing a transaction, another slight performance improvement can be achieved. Instead of waiting for the browser to commit an open transaction, we call the ",(0,i.jsx)(n.code,{children:"commit()"})," method to explicitly close it."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// .commit() is not available on all browsers, so first check if it exists."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"transaction"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".commit) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" transaction"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".commit"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["The improvement of this technique is minimal, but observable as ",(0,i.jsx)(n.a,{href:"https://nolanlawson.com/2021/08/22/speeding-up-indexeddb-reads-and-writes/",children:"these tests"})," show."]}),"\n",(0,i.jsx)(n.h2,{id:"in-memory-on-top-of-indexeddb",children:"In-Memory on top of IndexedDB"}),"\n",(0,i.jsxs)(n.p,{children:["To prevent transaction handling and to fix the performance problems, we need to stop using IndexedDB as a database. Instead all data is loaded into the memory on the initial page load. Here all reads and writes happen in memory which is about 100x faster. Only some time after a write occurred, the memory state is persisted into IndexedDB with a ",(0,i.jsx)(n.strong,{children:"single write transaction"}),". In this scenario IndexedDB is used as a filesystem, not as a database."]}),"\n",(0,i.jsx)(n.p,{children:"There are some libraries that already do that:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["LokiJS with the ",(0,i.jsx)(n.a,{href:"https://techfort.github.io/LokiJS/LokiIndexedAdapter.html",children:"IndexedDB Adapter"})]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/jlongster/absurd-sql",children:"Absurd-SQL"})}),"\n",(0,i.jsxs)(n.li,{children:["SQL.js with the ",(0,i.jsx)(n.a,{href:"https://emscripten.org/docs/api_reference/Filesystem-API.html#filesystem-api-idbfs",children:"empscripten Filesystem API"})]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://duckdb.org/2021/10/29/duckdb-wasm.html",children:"DuckDB Wasm"})}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"in-memory-persistence",children:"In-Memory: Persistence"}),"\n",(0,i.jsxs)(n.p,{children:["One downside of not directly using IndexedDB, is that your data is not persistent all the time. And when the JavaScript process exists without having persisted to IndexedDB, data can be lost. To prevent this from happening, we have to ensure that the in-memory state is written down to the disc. One point is make persisting as fast as possible. LokiJS for example has the ",(0,i.jsx)(n.code,{children:"incremental-indexeddb-adapter"})," which only saves new writes to the disc instead of persisting the whole state. Another point is to run the persisting at the correct point in time. For example the RxDB ",(0,i.jsx)(n.a,{href:"https://rxdb.info/rx-storage-lokijs.html",children:"LokiJS storage"})," persists in the following situations:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"When the database is idle and no write or query is running. In that time we can persist the state if any new writes appeared before."}),"\n",(0,i.jsxs)(n.li,{children:["When the ",(0,i.jsx)(n.code,{children:"window"})," fires the ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload",children:"beforeunload event"})," we can assume that the JavaScript process is exited any moment and we have to persist the state. After ",(0,i.jsx)(n.code,{children:"beforeunload"})," there are several seconds time which are sufficient to store all new changes. This has shown to work quite reliable."]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"The only missing event that can happen is when the browser exists unexpectedly like when it crashes or when the power of the computer is shut of."}),"\n",(0,i.jsx)(n.h3,{id:"in-memory-multi-tab-support",children:"In-Memory: Multi Tab Support"}),"\n",(0,i.jsx)(n.p,{children:"One big difference between a web application and a 'normal' app, is that your users can use the app in multiple browser tabs at the same time. But when you have all database state in memory and only periodically write it to disc, multiple browser tabs could overwrite each other and you would loose data. This might not be a problem when you rely on a client-server replication, because the lost data might already be replicated with the backend and therefore with the other tabs. But this would not work when the client is offline."}),"\n",(0,i.jsxs)(n.p,{children:["The ideal way to solve that problem, is to use a ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en/docs/Web/API/SharedWorker",children:"SharedWorker"}),". A ",(0,i.jsx)(n.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker"})," is like a ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en/docs/Web/API/Web_Workers_API",children:"WebWorker"})," that runs its own JavaScript process only that the SharedWorker is shared between multiple contexts. You could create the database in the SharedWorker and then all browser tabs could request the Worker for data instead of having their own database. But unfortunately the SharedWorker API does ",(0,i.jsx)(n.a,{href:"https://caniuse.com/sharedworkers",children:"not work"})," in all browsers. Safari ",(0,i.jsx)(n.a,{href:"https://bugs.webkit.org/show_bug.cgi?id=140344",children:"dropped"})," its support and InternetExplorer or Android Chrome, never adopted it. Also it cannot be polyfilled. ",(0,i.jsx)(n.strong,{children:"UPDATE:"})," ",(0,i.jsx)(n.a,{href:"https://developer.apple.com/safari/technology-preview/release-notes/",children:"Apple added SharedWorkers back in Safari 142"})]}),"\n",(0,i.jsxs)(n.p,{children:["Instead, we could use the ",(0,i.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API",children:"BroadcastChannel API"})," to communicate between tabs and then apply a ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/broadcast-channel#using-the-leaderelection",children:"leader election"})," between them. The ",(0,i.jsx)(n.a,{href:"/leader-election.html",children:"leader election"})," ensures that, no matter how many tabs are open, always one tab is the ",(0,i.jsx)(n.code,{children:"Leader"}),"."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/leader-election.gif",alt:"Leader Election",width:"500"})}),"\n",(0,i.jsx)(n.p,{children:"The disadvantage is that the leader election process takes some time on the initial page load (about 150 milliseconds). Also the leader election can break when a JavaScript process is fully blocked for a longer time. When this happens, a good way is to just reload the browser tab to restart the election process."}),"\n",(0,i.jsx)(n.h2,{id:"further-read",children:"Further read"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://github.com/pubkey/client-side-databases",children:"Offline First Database Comparison"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://nolanlawson.com/2021/08/22/speeding-up-indexeddb-reads-and-writes/",children:"Speeding up IndexedDB reads and writes"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://hackaday.com/2021/08/24/sqlite-on-the-web-absurd-sql/",children:"SQLITE ON THE WEB: ABSURD-SQL"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://anita-app.com/blog/articles/sqlite-in-a-pwa-with-file-system-access-api.html",children:"SQLite in a PWA with FileSystemAccessAPI"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"https://ravendb.net/articles/re-why-indexeddb-is-slow-and-what-to-use-instead",children:"Response to this article by Oren Eini"})}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8b4bf532.36358d72.js b/docs/assets/js/8b4bf532.36358d72.js
deleted file mode 100644
index 0e2518c0c54..00000000000
--- a/docs/assets/js/8b4bf532.36358d72.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4027],{7507(e,a,t){t.r(a),t.d(a,{default:()=>r});var i=t(544),n=t(4848);function r(){return(0,i.default)({sem:{id:"gads",metaTitle:"The NoSQL Database for JavaScript Applications",title:(0,n.jsxs)(n.Fragment,{children:["The easiest way to ",(0,n.jsx)("b",{children:"store"})," and ",(0,n.jsx)("b",{children:"sync"})," NoSQL Data"]}),text:(0,n.jsx)(n.Fragment,{children:"Store NoSQL data inside your App to build high performance realtime applications that sync data with the backend and even work when offline."})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8bc07e20.d1b7c7db.js b/docs/assets/js/8bc07e20.d1b7c7db.js
deleted file mode 100644
index 3bd656dc92e..00000000000
--- a/docs/assets/js/8bc07e20.d1b7c7db.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1850],{3247(e,s,r){r.d(s,{g:()=>i});var n=r(4848);function i(e){const s=[];let r=null;return e.children.forEach(e=>{e.props.id?(r&&s.push(r),r={headline:e,paragraphs:[]}):r&&r.paragraphs.push(e)}),r&&s.push(r),(0,n.jsx)("div",{style:a.stepsContainer,children:s.map((e,s)=>(0,n.jsxs)("div",{style:a.stepWrapper,children:[(0,n.jsxs)("div",{style:a.stepIndicator,children:[(0,n.jsx)("div",{style:a.stepNumber,children:s+1}),(0,n.jsx)("div",{style:a.verticalLine})]}),(0,n.jsxs)("div",{style:a.stepContent,children:[(0,n.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,n.jsx)("div",{style:a.item,children:e},s))]})]},s))})}const a={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},3628(e,s,r){r.r(s),r.d(s,{assets:()=>d,contentTitle:()=>c,default:()=>x,frontMatter:()=>l,metadata:()=>n,toc:()=>h});const n=JSON.parse('{"id":"capacitor-database","title":"Capacitor Database Guide - SQLite, RxDB & More","description":"Explore Capacitor\'s top data storage solutions - from key-value to real-time databases. Compare SQLite, RxDB, and more in this in-depth guide.","source":"@site/docs/capacitor-database.md","sourceDirName":".","slug":"/capacitor-database.html","permalink":"/capacitor-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Capacitor Database Guide - SQLite, RxDB & More","slug":"capacitor-database.html","description":"Explore Capacitor\'s top data storage solutions - from key-value to real-time databases. Compare SQLite, RxDB, and more in this in-depth guide."},"sidebar":"tutorialSidebar","previous":{"title":"IndexedDB Database in React Apps - The Power of RxDB","permalink":"/articles/react-indexeddb.html"},"next":{"title":"Alternatives for realtime local-first JavaScript applications and local databases","permalink":"/alternatives.html"}}');var i=r(4848),a=r(8453),o=r(2636),t=r(3247);const l={title:"Capacitor Database Guide - SQLite, RxDB & More",slug:"capacitor-database.html",description:"Explore Capacitor's top data storage solutions - from key-value to real-time databases. Compare SQLite, RxDB, and more in this in-depth guide."},c="Capacitor Database - SQLite, RxDB and others",d={},h=[{value:"Database Solutions for Capacitor",id:"database-solutions-for-capacitor",level:2},{value:"Preferences API",id:"preferences-api",level:3},{value:"Localstorage/IndexedDB/WebSQL",id:"localstorageindexeddbwebsql",level:3},{value:"SQLite",id:"sqlite",level:3},{value:"RxDB",id:"rxdb",level:3},{value:"Import RxDB and SQLite",id:"import-rxdb-and-sqlite",level:3},{value:"Import the RxDB SQLite Storage",id:"import-the-rxdb-sqlite-storage",level:3},{value:"RxDB Core",id:"rxdb-core",level:4},{value:"RxDB Premium \ud83d\udc51",id:"rxdb-premium-",level:4},{value:"Create a Database with the Storage",id:"create-a-database-with-the-storage",level:3},{value:"RxDB Core",id:"rxdb-core-1",level:4},{value:"RxDB Premium \ud83d\udc51",id:"rxdb-premium--1",level:4},{value:"Add a Collection",id:"add-a-collection",level:3},{value:"Insert a Document",id:"insert-a-document",level:3},{value:"Run a Query",id:"run-a-query",level:3},{value:"Observe a Query",id:"observe-a-query",level:3},{value:"Follow up",id:"follow-up",level:2}];function p(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"capacitor-database---sqlite-rxdb-and-others",children:"Capacitor Database - SQLite, RxDB and others"})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://capacitorjs.com/",children:"Capacitor"})," is an open source native JavaScript runtime to build Web based Native apps. You can use it to create cross-platform iOS, Android, and Progressive Web Apps with the web technologies JavaScript, HTML, and CSS.\nIt is developed by the Ionic Team and provides a great alternative to create hybrid apps. Compared to ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"}),", Capacitor is more Web-Like because the JavaScript runtime supports most Web APIs like IndexedDB, fetch, and so on."]}),"\n",(0,i.jsx)(s.p,{children:"To read and write persistent data in Capacitor, there are multiple solutions which are shown in the following."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/icons/capacitor.svg",alt:"Capacitor",width:"50"})}),"\n",(0,i.jsx)(s.h2,{id:"database-solutions-for-capacitor",children:"Database Solutions for Capacitor"}),"\n",(0,i.jsx)(s.h3,{id:"preferences-api",children:"Preferences API"}),"\n",(0,i.jsxs)(s.p,{children:["Capacitor comes with a native ",(0,i.jsx)(s.a,{href:"https://capacitorjs.com/docs/apis/preferences",children:"Preferences API"})," which is a simple, persistent key->value store for lightweight data, similar to the browsers localstorage or React Native ",(0,i.jsx)(s.a,{href:"/react-native-database.html#asyncstorage",children:"AsyncStorage"}),"."]}),"\n",(0,i.jsxs)(s.p,{children:["To use it, you first have to install it from npm ",(0,i.jsx)(s.code,{children:"npm install @capacitor/preferences"})," and then you can import it and write/read data.\nNotice that all calls to the preferences API are asynchronous so they return a ",(0,i.jsx)(s.code,{children:"Promise"})," that must be ",(0,i.jsx)(s.code,{children:"await"}),"-ed."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Preferences } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@capacitor/preferences'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// write"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Preferences"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".set"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" value"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'baar'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// read"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"value"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Preferences"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }); "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > 'bar'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// delete"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Preferences"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})]})]})})}),"\n",(0,i.jsx)(s.p,{children:"The preferences API is good when only a small amount of data needs to be stored and when no query capabilities besides the key access are required. Complex queries or other features like indexes or replication are not supported which makes the preferences API not suitable for anything more than storing simple data like user settings."}),"\n",(0,i.jsx)(s.h3,{id:"localstorageindexeddbwebsql",children:"Localstorage/IndexedDB/WebSQL"}),"\n",(0,i.jsxs)(s.p,{children:["Since Capacitor apps run in a web view, Web APIs like IndexedDB, ",(0,i.jsx)(s.a,{href:"/articles/localstorage.html",children:"Localstorage"})," and WebSQL are available. But the default browser behavior is to clean up these storages regularly when they are not in use for a long time or the device is low on space. Therefore you cannot 100% rely on the persistence of the stored data and your application needs to expect that the data will be lost eventually."]}),"\n",(0,i.jsx)(s.p,{children:"Storing data in these storages can be done in browsers, because there is no other option. But in Capacitor iOS and Android, you should not rely on these."}),"\n",(0,i.jsx)(s.h3,{id:"sqlite",children:"SQLite"}),"\n",(0,i.jsx)(s.p,{children:"SQLite is a SQL based relational database written in C that was crafted to be embed inside of applications. Operations are written in the SQL query language and SQLite generally follows the PostgreSQL syntax."}),"\n",(0,i.jsx)(s.p,{children:"To use SQLite in Capacitor, there are three options:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["The ",(0,i.jsx)(s.a,{href:"https://github.com/capacitor-community/sqlite",children:"@capacitor-community/sqlite"})," package"]}),"\n",(0,i.jsxs)(s.li,{children:["The ",(0,i.jsx)(s.a,{href:"https://github.com/storesafe/cordova-sqlite-storage",children:"cordova-sqlite-storage"})," package"]}),"\n",(0,i.jsxs)(s.li,{children:["The non-free ",(0,i.jsx)(s.a,{href:"/articles/ionic-database.html",children:"Ionic"})," ",(0,i.jsx)(s.a,{href:"https://ionic.io/products/secure-storage",children:"Secure Storage"})," which comes at ",(0,i.jsx)(s.strong,{children:"999$"})," per month."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["It is recommended to use the ",(0,i.jsx)(s.code,{children:"@capacitor-community/sqlite"})," because it has the best maintenance and is open source. Install it first ",(0,i.jsx)(s.code,{children:"npm install --save @capacitor-community/sqlite"})," and then set the storage location for iOS apps:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "plugins"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "CapacitorSQLite"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "iosDatabaseLocation"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Library/CapacitorDatabase"'})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"Now you can create a database connection and use the SQLite database."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Capacitor } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@capacitor/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" CapacitorSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLiteDBConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLiteConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" capSQLiteSet"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" capSQLiteChanges"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" capSQLiteValues"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" capEchoResult"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" capSQLiteResult"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" capNCDatabasePathResult"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@capacitor-community/sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" SQLiteConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(CapacitorSQLite);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" SQLiteDBConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".createConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" databaseName"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encrypted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mode"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" readOnly"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { rows } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".query"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'SELECT somevalue FROM sometable'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,i.jsx)(s.p,{children:"The downside of SQLite is that it is lacking many features that are handful when using a database together with an UI based application like your Capacitor app. For example it is not possible to observe queries or document fields. Also there is no realtime replication feature, you can only import json files. This makes SQLite a good solution when you just want to store data on the client, but when you want to sync data with a server or other clients or create big complex realtime applications, you have to use something else."}),"\n",(0,i.jsx)(s.h3,{id:"rxdb",children:"RxDB"}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/logo/rxdb_javascript_database.svg",alt:"RxDB",width:"170"})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," is an local first, NoSQL database for JavaScript Applications like hybrid apps. Because it is reactive, you can subscribe to all state changes like the result of a query or even a single field of a document. This is great for UI-based realtime applications in a way that makes it easy to develop realtime applications like what you need in Capacitor."]}),"\n",(0,i.jsxs)(s.p,{children:["Because RxDB is made for Web applications, most of the ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"available RxStorage"})," plugins can be used to store and query data in a Capacitor app. However it is recommended to use the ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"})," because it stores the data on the filesystem of the device, not in the JavaScript runtime (like IndexedDB). Storing data on the filesystem ensures it is persistent and will not be cleaned up by any process. Also the performance of SQLite is ",(0,i.jsx)(s.a,{href:"/rx-storage.html#performance-comparison",children:"much faster"})," compared to IndexedDB, because SQLite does not have to go through a browsers permission layers. For the SQLite binding you should use the ",(0,i.jsx)(s.a,{href:"https://github.com/capacitor-community/sqlite",children:"@capacitor-community/sqlite"})," package."]}),"\n",(0,i.jsxs)(s.p,{children:["Because the SQLite RxStorage is part of the ",(0,i.jsx)(s.a,{href:"/premium/",children:"\ud83d\udc51 Premium Plugins"})," which must be purchased, it is recommended to use the ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"})," while testing and prototyping your Capacitor app."]}),"\n",(0,i.jsxs)(s.p,{children:["To use the SQLite RxStorage in Capacitor you have to install all dependencies via ",(0,i.jsx)(s.code,{children:"npm install rxdb rxjs rxdb-premium @capacitor-community/sqlite"}),"."]}),"\n",(0,i.jsx)(s.p,{children:"For iOS apps you should add a database location in your Capacitor settings:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "plugins"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "CapacitorSQLite"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "iosDatabaseLocation"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "Library/CapacitorDatabase"'})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"Then you can assemble the RxStorage and create a database with it:"}),"\n",(0,i.jsxs)(t.g,{children:[(0,i.jsx)(s.h3,{id:"import-rxdb-and-sqlite",children:"Import RxDB and SQLite"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" CapacitorSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" SQLiteConnection"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@capacitor-community/sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Capacitor } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@capacitor/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" SQLiteConnection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(CapacitorSQLite);"})]})]})})}),(0,i.jsx)(s.h3,{id:"import-the-rxdb-sqlite-storage",children:"Import the RxDB SQLite Storage"}),(0,i.jsxs)(o.t,{children:[(0,i.jsx)(s.h4,{id:"rxdb-core",children:"RxDB Core"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLiteTrial"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsCapacitor"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h4,{id:"rxdb-premium-",children:"RxDB Premium \ud83d\udc51"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getSQLiteBasicsCapacitor"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sqlite'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})})]}),(0,i.jsx)(s.h3,{id:"create-a-database-with-the-storage",children:"Create a Database with the Storage"}),(0,i.jsxs)(o.t,{children:[(0,i.jsx)(s.h4,{id:"rxdb-core-1",children:"RxDB Core"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create database"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLiteTrial"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsCapacitor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Capacitor)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h4,{id:"rxdb-premium--1",children:"RxDB Premium \ud83d\udc51"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create database"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSQLite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sqliteBasics"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getSQLiteBasicsCapacitor"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sqlite"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" Capacitor)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]}),(0,i.jsx)(s.h3,{id:"add-a-collection",children:"Add a Collection"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create collections"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(s.h3,{id:"insert-a-document",children:"Insert a Document"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foo'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]})})})}),(0,i.jsx)(s.h3,{id:"run-a-query",children:"Run a Query"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),(0,i.jsx)(s.h3,{id:"observe-a-query",children:"Observe a Query"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"humans"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bar'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(result "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]})]})})})]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow up"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["If you haven't done yet, you should start learning about RxDB with the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"Quickstart Tutorial"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["There is a followup list of other ",(0,i.jsx)(s.a,{href:"/alternatives.html",children:"client side database alternatives"}),"."]}),"\n"]})]})}function x(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453(e,s,r){r.d(s,{R:()=>o,x:()=>t});var n=r(6540);const i={},a=n.createContext(i);function o(e){const s=n.useContext(a);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),n.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8bc82b1f.9df61a79.js b/docs/assets/js/8bc82b1f.9df61a79.js
deleted file mode 100644
index 26fd09299ed..00000000000
--- a/docs/assets/js/8bc82b1f.9df61a79.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9997],{2591(e,n,s){s.r(n),s.d(n,{assets:()=>t,contentTitle:()=>a,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"rx-pipeline","title":"RxPipeline - Automate Data Flows in RxDB","description":"Discover how RxPipeline automates your data workflows. Seamlessly process writes, manage leader election, and ensure crash-safe operations in RxDB.","source":"@site/docs/rx-pipeline.md","sourceDirName":".","slug":"/rx-pipeline.html","permalink":"/rx-pipeline.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxPipeline - Automate Data Flows in RxDB","slug":"rx-pipeline.html","description":"Discover how RxPipeline automates your data workflows. Seamlessly process writes, manage leader election, and ensure crash-safe operations in RxDB."},"sidebar":"tutorialSidebar","previous":{"title":"Attachments","permalink":"/rx-attachment.html"},"next":{"title":"Custom Reactivity","permalink":"/reactivity.html"}}');var i=s(4848),o=s(8453);const l={title:"RxPipeline - Automate Data Flows in RxDB",slug:"rx-pipeline.html",description:"Discover how RxPipeline automates your data workflows. Seamlessly process writes, manage leader election, and ensure crash-safe operations in RxDB."},a="RxPipeline",t={},c=[{value:"Creating a RxPipeline",id:"creating-a-rxpipeline",level:2},{value:"Use Cases for RxPipeline",id:"use-cases-for-rxpipeline",level:2},{value:"UseCase: Re-Index data that comes from replication",id:"usecase-re-index-data-that-comes-from-replication",level:3},{value:"UseCase: Fulltext Search",id:"usecase-fulltext-search",level:3},{value:"UseCase: Download data based on source documents",id:"usecase-download-data-based-on-source-documents",level:3},{value:"RxPipeline methods",id:"rxpipeline-methods",level:2},{value:"awaitIdle()",id:"awaitidle",level:3},{value:"close()",id:"close",level:3},{value:"remove()",id:"remove",level:3},{value:"Using RxPipeline correctly",id:"using-rxpipeline-correctly",level:2},{value:"Pipeline handlers must be idempotent",id:"pipeline-handlers-must-be-idempotent",level:3},{value:"Pipeline handlers must not throw",id:"pipeline-handlers-must-not-throw",level:3},{value:"Be careful when doing http requests in the handler",id:"be-careful-when-doing-http-requests-in-the-handler",level:3},{value:"Pipelines temporarily block external reads and writes",id:"pipelines-temporarily-block-external-reads-and-writes",level:3}];function d(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"rxpipeline",children:"RxPipeline"})}),"\n",(0,i.jsx)(n.p,{children:"The RxPipeline plugin enables you to run operations depending on writes to a collection.\nWhenever a write happens on the source collection of a pipeline, a handler is called to process the writes and run operations on another collection."}),"\n",(0,i.jsx)(n.p,{children:"You could have a similar behavior by observing the collection stream and process data on emits:"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"mySourceCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"$"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(event "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ...process...*/"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]})})})}),"\n",(0,i.jsx)(n.p,{children:"While this could work in some cases, it causes many problems that are fixed by using the pipeline plugin instead:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["In an RxPipeline, only the ",(0,i.jsx)(n.a,{href:"/leader-election.html",children:"Leading Instance"})," runs the operations. For example when you have multiple browser tabs open, only one will run the processing and when that tab is closed, another tab will become elected leader and continue the pipeline processing."]}),"\n",(0,i.jsx)(n.li,{children:"On sudden stops and restarts of the JavaScript process, the processing will continue at the correct checkpoint and not miss out any documents even on unexpected crashes."}),"\n",(0,i.jsx)(n.li,{children:"Reads/Writes on the destination collection are halted while the pipeline is processing. This ensures your queries only return fully processed documents and no partial results. So when you run a query to the destination collection directly after a write to the source collection, you can be sure your query results are up to date and the pipeline has already been run at the moment the query resolved:"}),"\n"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mySourceCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Because our pipeline blocks reads to the destination,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * we know that the result array contains data created"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * on top of the previously inserted documents."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" result"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDestinationCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(n.h2,{id:"creating-a-rxpipeline",children:"Creating a RxPipeline"}),"\n",(0,i.jsxs)(n.p,{children:["Pipelines are created on top of a source ",(0,i.jsx)(n.a,{href:"/rx-collection.html",children:"RxCollection"})," and have another ",(0,i.jsx)(n.code,{children:"RxCollection"})," as destination. An identifier is used to identify the state of the pipeline so that different pipelines have a different processing checkpoint state. A plain JavaScript function ",(0,i.jsx)(n.code,{children:"handler"})," is used to process the data of the source collection writes."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mySourceCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-pipeline'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myDestinationCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here you can process the documents and write to"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * the destination collection."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" for"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" of"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDestinationCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".insert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".primary"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" category"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".category"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"use-cases-for-rxpipeline",children:"Use Cases for RxPipeline"}),"\n",(0,i.jsx)(n.p,{children:"The RxPipeline is a handy building block for different features and plugins. You can use it to aggregate data or restructure local data."}),"\n",(0,i.jsx)(n.h3,{id:"usecase-re-index-data-that-comes-from-replication",children:"UseCase: Re-Index data that comes from replication"}),"\n",(0,i.jsxs)(n.p,{children:["Sometimes you want to ",(0,i.jsx)(n.a,{href:"/replication.html",children:"replicate"})," atomic documents over the wire but locally you want to split these documents for better indexing. For example you replicate email documents that have multiple receivers in a string-array. While string-arrays cannot be indexed, locally you need a way to query for all emails of a given receiver.\nTo handle this case you can set up a RxPipeline that writes the mapping into a separate collection:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" emailCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'map-email-receivers'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" emailByReceiverCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" for"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" of"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // remove previous mapping"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" emailByReceiverCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({emailId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".primary})"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // add new mapping"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".deleted) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" emailByReceiverCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".bulkInsert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"receivers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(receiver "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" emailId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".primary"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" receiver"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" receiver"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }))"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.p,{children:'With this you can efficiently query for "all emails that a person received" by running:'}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mailIds"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" emailByReceiverCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" receiver"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar@example.com'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(n.h3,{id:"usecase-fulltext-search",children:"UseCase: Fulltext Search"}),"\n",(0,i.jsx)(n.p,{children:"You can utilize the pipeline plugin to index text data for efficient fulltext search."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" emailCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'email-fulltext-search'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mailByWordCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" for"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" of"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // remove previous mapping"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mailByWordCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({emailId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".primary})"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // add new mapping"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".deleted) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" words"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"text"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".split"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"' '"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mailByWordCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".bulkInsert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" words"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(word "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" emailId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".primary"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" word"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" word"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }))"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.p,{children:'With this you can efficiently query for "all emails that contain a given word" by running:'}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mailIds"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" emailByReceiverCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({word"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'foobar'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"})"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),"\n",(0,i.jsx)(n.h3,{id:"usecase-download-data-based-on-source-documents",children:"UseCase: Download data based on source documents"}),"\n",(0,i.jsx)(n.p,{children:"When you have to fetch data for each document of a collection from a server, you can use the pipeline to ensure all documents have their data downloaded and no document is missed."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" emailCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'download-data'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" serverDataCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" for"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" of"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'https://example.com/doc/'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".primary);"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" serverData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" serverDataCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".upsert"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".primary"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" data"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" serverData"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h2,{id:"rxpipeline-methods",children:"RxPipeline methods"}),"\n",(0,i.jsx)(n.h3,{id:"awaitidle",children:"awaitIdle()"}),"\n",(0,i.jsxs)(n.p,{children:["You can await the idleness of a pipeline with ",(0,i.jsx)(n.code,{children:"await myRxPipeline.awaitIdle()"}),". This will await a promise that resolves when the pipeline has processed all documents and is not running anymore."]}),"\n",(0,i.jsx)(n.h3,{id:"close",children:"close()"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"await myRxPipeline.close()"})," stops the pipeline so that it is no longer doing stuff. This is automatically called when the RxCollection or RxDatabase of the pipeline is closed."]}),"\n",(0,i.jsx)(n.h3,{id:"remove",children:"remove()"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"await myRxPipeline.remove()"})," removes the pipeline and all metadata which it has stored. Recreating the pipeline afterwards will start processing all source documents from scratch."]}),"\n",(0,i.jsx)(n.h2,{id:"using-rxpipeline-correctly",children:"Using RxPipeline correctly"}),"\n",(0,i.jsx)(n.h3,{id:"pipeline-handlers-must-be-idempotent",children:"Pipeline handlers must be idempotent"}),"\n",(0,i.jsx)(n.p,{children:"Because a JavaScript process can exit at any time, like when the user closes a browser tab, the pipeline handler function must be idempotent. This means when it only runs partially and is started again with the same input, it should still end up in the correct result."}),"\n",(0,i.jsx)(n.h3,{id:"pipeline-handlers-must-not-throw",children:"Pipeline handlers must not throw"}),"\n",(0,i.jsxs)(n.p,{children:["Pipeline handlers must never throw. If you run operations inside of the handler that might cause errors, you must wrap the handler's code with a ",(0,i.jsx)(n.code,{children:"try-catch"})," by yourself and also handle retries. If your handler throws, your pipeline will be stuck and no longer be usable, which should never happen."]}),"\n",(0,i.jsx)(n.h3,{id:"be-careful-when-doing-http-requests-in-the-handler",children:"Be careful when doing http requests in the handler"}),"\n",(0,i.jsxs)(n.p,{children:["When you run http requests inside of your handler, you no longer have an ",(0,i.jsx)(n.a,{href:"/offline-first.html",children:"offline first"})," application because reads to the destination collection will be blocked until all handlers have finished. When your client is offline, therefore the collection will be blocked for reads and writes."]}),"\n",(0,i.jsx)(n.h3,{id:"pipelines-temporarily-block-external-reads-and-writes",children:"Pipelines temporarily block external reads and writes"}),"\n",(0,i.jsxs)(n.p,{children:["While a pipeline is running, ",(0,i.jsx)(n.strong,{children:"all reads and writes to its destination collection are blocked"}),". This guarantees that queries never observe partially processed data, but it also means that pipelines can block each other if they interact incorrectly."]}),"\n",(0,i.jsx)(n.p,{children:"Problems occur when multiple pipelines:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"read or write across the same collections, or"}),"\n",(0,i.jsxs)(n.li,{children:["wait for each other using ",(0,i.jsx)(n.code,{children:"awaitIdle()"})," from inside a pipeline handler."]}),"\n"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Example of a deadlock"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Pipeline A: files \u2192 files (reads folders)"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pipelineA"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"files"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'file-path-sync'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".files"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" folders"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" folders"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// can block"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Pipeline B: files \u2192 folders (waits for A)"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"folders"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'file-count'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".folders"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" pipelineA"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".awaitIdle"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// \u274c may deadlock"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.p,{children:"To prevent deadlocks, consider:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Never call ",(0,i.jsx)(n.code,{children:"awaitIdle()"})," inside a pipeline handler."]}),"\n",(0,i.jsx)(n.li,{children:"Avoid circular dependencies between pipelines."}),"\n",(0,i.jsx)(n.li,{children:"Prefer one-directional data flow."}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>l,x:()=>a});var r=s(6540);const i={},o=r.createContext(i);function l(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:l(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/8bfd920a.7d84a9e9.js b/docs/assets/js/8bfd920a.7d84a9e9.js
deleted file mode 100644
index 7847091bdad..00000000000
--- a/docs/assets/js/8bfd920a.7d84a9e9.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2631],{8453(e,s,n){n.d(s,{R:()=>l,x:()=>a});var r=n(6540);const i={},o=r.createContext(i);function l(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function a(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:l(e.components),r.createElement(o.Provider,{value:s},e.children)}},9678(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>a,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/ionic-storage","title":"RxDB - Local Ionic Storage with Encryption, Compression & Sync","description":"The best Ionic storage solution? RxDB empowers your hybrid apps with offline-first capabilities, secure encryption, data compression, and seamless data syncing to any backend.","source":"@site/docs/articles/ionic-storage.md","sourceDirName":"articles","slug":"/articles/ionic-storage.html","permalink":"/articles/ionic-storage.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB - Local Ionic Storage with Encryption, Compression & Sync","slug":"ionic-storage.html","description":"The best Ionic storage solution? RxDB empowers your hybrid apps with offline-first capabilities, secure encryption, data compression, and seamless data syncing to any backend."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB - The Perfect Ionic Database","permalink":"/articles/ionic-database.html"},"next":{"title":"RxDB - The JSON Database Built for JavaScript","permalink":"/articles/json-database.html"}}');var i=n(4848),o=n(8453);const l={title:"RxDB - Local Ionic Storage with Encryption, Compression & Sync",slug:"ionic-storage.html",description:"The best Ionic storage solution? RxDB empowers your hybrid apps with offline-first capabilities, secure encryption, data compression, and seamless data syncing to any backend."},a="RxDB - Local Ionic Storage with Encryption, Compression & Sync",t={},c=[{value:"Why RxDB for Ionic Storage?",id:"why-rxdb-for-ionic-storage",level:2},{value:"1. Offline-Ready NoSQL Storage",id:"1-offline-ready-nosql-storage",level:3},{value:"2. Powerful Encryption",id:"2-powerful-encryption",level:3},{value:"3. Built-In Data Compression",id:"3-built-in-data-compression",level:3},{value:"4. Real-Time Sync & Conflict Handling",id:"4-real-time-sync--conflict-handling",level:3},{value:"5. Easy to Adopt and Extend",id:"5-easy-to-adopt-and-extend",level:3},{value:"Quick Start: Implementing RxDB with LocalSTorage Storage",id:"quick-start-implementing-rxdb-with-localstorage-storage",level:2},{value:"Encryption Example",id:"encryption-example",level:2},{value:"Compression Example",id:"compression-example",level:2},{value:"RxDB vs. Other Ionic Storage Options",id:"rxdb-vs-other-ionic-storage-options",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"rxdb---local-ionic-storage-with-encryption-compression--sync",children:"RxDB - Local Ionic Storage with Encryption, Compression & Sync"})}),"\n",(0,i.jsxs)(s.p,{children:["When building ",(0,i.jsx)(s.strong,{children:"Ionic"})," apps, developers face the challenge of choosing a robust ",(0,i.jsx)(s.strong,{children:"Ionic storage"})," mechanism that supports:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline-First"})," usage"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Data Encryption"})," to protect sensitive content"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Compression"})," to reduce storage usage and improve performance"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Seamless Sync"})," with any backend for real-time updates"]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," (Reactive Database) offers all these features in a single, ",(0,i.jsx)(s.a,{href:"/articles/local-first-future.html",children:"local-first"})," database solution tailored to ",(0,i.jsx)(s.strong,{children:"Ionic"})," and other hybrid frameworks. Keep reading to learn how RxDB solves the most common storage pitfalls in hybrid app development while providing unmatched flexibility."]}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/icons/ionic.svg",alt:"Ionic Database Storage",width:"120"})})}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsx)(s.h2,{id:"why-rxdb-for-ionic-storage",children:"Why RxDB for Ionic Storage?"}),"\n",(0,i.jsx)(s.h3,{id:"1-offline-ready-nosql-storage",children:"1. Offline-Ready NoSQL Storage"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"/offline-first.html",children:"Offline functionality"})," is crucial for modern mobile applications, particularly when devices encounter unreliable or slow networks. RxDB stores all data ",(0,i.jsx)(s.strong,{children:"locally"})," so your Ionic app can run seamlessly without needing a continuous internet connection. When a network is available again, RxDB automatically synchronizes changes with your backend - no extra code required."]}),"\n",(0,i.jsx)(s.h3,{id:"2-powerful-encryption",children:"2. Powerful Encryption"}),"\n",(0,i.jsxs)(s.p,{children:["Securing on-device data is paramount when handling sensitive information. RxDB includes ",(0,i.jsx)(s.a,{href:"../encryption.html",children:"encryption plugins"})," that let you:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Encrypt"})," data fields at rest with AES"]}),"\n",(0,i.jsx)(s.li,{children:"Invalidate data access by simply withholding the password"}),"\n",(0,i.jsx)(s.li,{children:"Keep your users' data confidential, even if the device is stolen"}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"This built-in encryption sets RxDB apart from many other Ionic storage options that lack integrated security."}),"\n",(0,i.jsx)(s.h3,{id:"3-built-in-data-compression",children:"3. Built-In Data Compression"}),"\n",(0,i.jsxs)(s.p,{children:["Large or repetitive data can significantly slow down devices with minimal memory. RxDB's ",(0,i.jsx)(s.a,{href:"/key-compression.html",children:"key-compression"})," feature decreases document size stored on the device, improving overall performance by:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Reducing disk usage"}),"\n",(0,i.jsx)(s.li,{children:"Accelerating queries"}),"\n",(0,i.jsx)(s.li,{children:"Minimizing network overhead when syncing"}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"4-real-time-sync--conflict-handling",children:"4. Real-Time Sync & Conflict Handling"}),"\n",(0,i.jsxs)(s.p,{children:["In addition to functioning fully offline, RxDB supports advanced ",(0,i.jsx)(s.a,{href:"/replication.html",children:"replication"})," options. Your Ionic app can instantly sync updates with any backend (",(0,i.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB"}),", ",(0,i.jsx)(s.a,{href:"/replication-firestore.html",children:"Firestore"}),", ",(0,i.jsx)(s.a,{href:"/replication-graphql.html",children:"GraphQL"}),", or ",(0,i.jsx)(s.a,{href:"/replication-http.html",children:"custom REST"}),"), maintaining a ",(0,i.jsx)(s.a,{href:"/articles/realtime-database.html",children:"real-time"})," user experience. Plus, RxDB handles ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"conflicts"})," gracefully - meaning less worry about clashing user edits."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/database-replication.png",alt:"database replication",width:"200"})}),"\n",(0,i.jsx)(s.h3,{id:"5-easy-to-adopt-and-extend",children:"5. Easy to Adopt and Extend"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB runs with a ",(0,i.jsx)(s.strong,{children:"NoSQL"})," approach and integrates seamlessly into ",(0,i.jsx)(s.a,{href:"https://ionicframework.com/docs/angular/overview",children:"Ionic Angular"})," or other frameworks you might use with Ionic. You can extend or replace storage backends, add encryption, or build advanced offline-first features with minimal overhead."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"Ionic Storage Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"quick-start-implementing-rxdb-with-localstorage-storage",children:"Quick Start: Implementing RxDB with LocalSTorage Storage"}),"\n",(0,i.jsxs)(s.p,{children:["For a simple proof-of-concept or testing environment in ",(0,i.jsx)(s.a,{href:"/articles/ionic-database.html",children:"Ionic"}),", you can use ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"localstorage"})," as your underlying storage. Later, if you need better native performance, you can ",(0,i.jsx)(s.strong,{children:"switch to the SQLite storage"})," offered by the ",(0,i.jsx)(s.a,{href:"https://rxdb.info/premium/",children:"RxDB Premium plugins"}),"."]}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.strong,{children:"Install RxDB"})}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"})]})})})}),"\n",(0,i.jsxs)(s.ol,{start:"2",children:["\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.strong,{children:"Initialize the Database"})}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myionicdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // or true if you plan multi-tab usage"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Note: If you need encryption, set `password` here"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" notes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'notes schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" content"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" timestamp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(s.ol,{start:"3",children:["\n",(0,i.jsx)(s.li,{children:(0,i.jsx)(s.strong,{children:"Ready to Upgrade Later?"})}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["When you need the best performance on mobile devices, purchase the RxDB ",(0,i.jsx)(s.a,{href:"/premium/",children:"Premium"})," ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite Storage"})," and replace ",(0,i.jsx)(s.code,{children:"getRxStorageLocalstorage()"})," with ",(0,i.jsx)(s.code,{children:"getRxStorageSQLite()"})," - your app logic remains largely the same. You only have to change the configuration."]}),"\n",(0,i.jsx)(s.h2,{id:"encryption-example",children:"Encryption Example"}),"\n",(0,i.jsxs)(s.p,{children:["To secure local data, add the crypto-js ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"encryption plugin"})," (free version) or the ",(0,i.jsx)(s.a,{href:"/premium/",children:"premium"})," web-crypto plugin. Below is an example using the free crypto-js plugin:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedKeyEncryptionCryptoJsStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/encryption-crypto-js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initEncryptedDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" encryptedStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyEncryptionCryptoJsStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'secureIonicDB'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encryptedStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myS3cretP4ssw0rd'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" secrets"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'secret schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // all fields in this array will be stored encrypted:"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encrypted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'text'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"With encryption enabled:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.code,{children:"text"})," is automatically encrypted at rest."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-query.html",children:"Queries"})," on encrypted fields are not directly possible (since data is encrypted), but once a document is loaded, RxDB decrypts it for normal usage."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"compression-example",children:"Compression Example"}),"\n",(0,i.jsxs)(s.p,{children:["To minimize the storage footprint, RxDB offers a ",(0,i.jsx)(s.a,{href:"/key-compression.html",children:"key-compression"})," feature. You can enable it in your schema:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" logs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'logs schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" keyCompression"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // enable compression"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" message"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createdAt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" format"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'date-time'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["With ",(0,i.jsx)(s.code,{children:"keyCompression: true"}),", RxDB shortens field names internally, significantly reducing document size. This helps both stored data and network transport during replication."]}),"\n",(0,i.jsx)(s.h2,{id:"rxdb-vs-other-ionic-storage-options",children:"RxDB vs. Other Ionic Storage Options"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Ionic Native Storage"})," or ",(0,i.jsx)(s.strong,{children:"Capacitor-based"})," key-value stores may handle small amounts of data but lack advanced features like:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Complex queries"}),"\n",(0,i.jsx)(s.li,{children:"Full NoSQL document model"}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/offline-first.html",children:"Offline-first"})," ",(0,i.jsx)(s.a,{href:"/replication.html",children:"sync"})]}),"\n",(0,i.jsx)(s.li,{children:"Encryption & key compression out of the box"}),"\n",(0,i.jsx)(s.li,{children:"RxDB stands out by delivering all these capabilities in a unified library."}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.p,{children:["For Ionic storage that supports offline-first operations, built-in encryption, optional data compression, and live syncing with any backend, RxDB provides a powerful solution. Start quickly with ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"localstorage"})," for local development and testing - then scale up to the premium SQLite storage for optimal performance on production mobile devices."]}),"\n",(0,i.jsx)(s.p,{children:"Ready to learn more?"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Explore the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart Guide"})]}),"\n",(0,i.jsxs)(s.li,{children:["Check out ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"RxDB Encryption"})," to protect user data"]}),"\n",(0,i.jsxs)(s.li,{children:["Learn about ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite Storage"})," in ",(0,i.jsx)(s.a,{href:"/premium/",children:"RxDB Premium"})," for top ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance"})," on mobile."]}),"\n",(0,i.jsxs)(s.li,{children:["Join our community on the ",(0,i.jsx)(s.a,{href:"/chat/",children:"RxDB Chat"})]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"RxDB"})," - The ultimate toolkit for Ionic developers seeking offline-first, secure, and compressed local data, with real-time sync to any server."]})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/90102fdf.191788f5.js b/docs/assets/js/90102fdf.191788f5.js
deleted file mode 100644
index 6fb54b6d969..00000000000
--- a/docs/assets/js/90102fdf.191788f5.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[2633],{9935(e,a,t){t.r(a),t.d(a,{default:()=>r});var s=t(544),n=t(4848);function r(){return(0,s.default)({sem:{id:"gads",metaTitle:"The local Database for Node.js",appName:"Node.js",title:(0,n.jsxs)(n.Fragment,{children:["The easiest way to ",(0,n.jsx)("b",{children:"store"})," and ",(0,n.jsx)("b",{children:"sync"})," Data in Node.js"]}),text:(0,n.jsx)(n.Fragment,{children:"Fast in-app database for Node.js. Build high performance realtime applications that sync data from the anywhere and even work when offline."})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/9187.d599c63b.js b/docs/assets/js/9187.d599c63b.js
deleted file mode 100644
index ae345c96f1c..00000000000
--- a/docs/assets/js/9187.d599c63b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9187],{9187(b,h,s){s.r(h)}}]);
\ No newline at end of file
diff --git a/docs/assets/js/91b454ee.57807487.js b/docs/assets/js/91b454ee.57807487.js
deleted file mode 100644
index b4d2e4d6ae7..00000000000
--- a/docs/assets/js/91b454ee.57807487.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4202],{449(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"rx-storage-sharding","title":"Sharding RxStorage \ud83d\udc51","description":"With the sharding plugin, you can improve the write and query times of some RxStorage implementations.","source":"@site/docs/rx-storage-sharding.md","sourceDirName":".","slug":"/rx-storage-sharding.html","permalink":"/rx-storage-sharding.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Sharding RxStorage \ud83d\udc51","slug":"rx-storage-sharding.html"},"sidebar":"tutorialSidebar","previous":{"title":"Memory Mapped RxStorage \ud83d\udc51","permalink":"/rx-storage-memory-mapped.html"},"next":{"title":"Localstorage Meta Optimizer \ud83d\udc51","permalink":"/rx-storage-localstorage-meta-optimizer.html"}}');var a=s(4848),i=s(8453);const o={title:"Sharding RxStorage \ud83d\udc51",slug:"rx-storage-sharding.html"},t="Sharding RxStorage",l={},d=[{value:"Using the sharding plugin",id:"using-the-sharding-plugin",level:2}];function c(e){const n={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",strong:"strong",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"sharding-rxstorage",children:"Sharding RxStorage"})}),"\n",(0,a.jsxs)(n.p,{children:["With the sharding plugin, you can improve the write and query times of ",(0,a.jsx)(n.strong,{children:"some"})," ",(0,a.jsx)(n.code,{children:"RxStorage"})," implementations.\nFor example on ",(0,a.jsx)(n.a,{href:"/slow-indexeddb.html",children:"slow IndexedDB"}),", a performance gain of ",(0,a.jsx)(n.strong,{children:"30-50% on reads"}),", and ",(0,a.jsx)(n.strong,{children:"25% on writes"})," can be achieved by using multiple IndexedDB Stores instead of putting all documents into the same store."]}),"\n",(0,a.jsxs)(n.p,{children:["The sharding plugin works as a wrapper around any other ",(0,a.jsx)(n.code,{children:"RxStorage"}),". The sharding plugin will automatically create multiple shards per storage instance and it will merge and split read and write calls to it."]}),"\n",(0,a.jsx)(n.admonition,{title:"Premium",type:"note",children:(0,a.jsxs)(n.p,{children:["The sharding plugin is part of ",(0,a.jsx)(n.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"}),". It is not part of the default RxDB module."]})}),"\n",(0,a.jsx)(n.h2,{id:"using-the-sharding-plugin",children:"Using the sharding plugin"}),"\n",(0,a.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageSharding"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-sharding'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * First wrap the original RxStorage with the sharding RxStorage."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" shardedRxStorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageSharding"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Here we use the localStorage RxStorage,"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * it is also possible to use any other RxStorage instead."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Add the sharding options to your schema."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Changing these options will require a data migration."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mySchema"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" sharding"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Amount of shards per RxStorage instance."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Depending on your data size and query patterns, the optimal shard amount may differ."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Do a performance test to optimize that value."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * 10 Shards is a good value to start with."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * "})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * IMPORTANT: Changing the value of shards is not possible on a already existing database state,"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * you will loose access to your data."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" shards"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Sharding mode,"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * you can either shard by collection or by database."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * For most cases you should use 'collection' which will shard on the collection level."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * For example with the IndexedDB RxStorage, it will then create multiple stores per IndexedDB database"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * and not multiple IndexedDB databases, which would be slower."})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mode"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'collection'"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Create the RxDatabase with the wrapped RxStorage. "})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(n.span,{"data-line":"",children:[(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" shardedRxStorage"})]}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:(0,a.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(n.span,{"data-line":"",children:" "})]})})})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},8453(e,n,s){s.d(n,{R:()=>o,x:()=>t});var r=s(6540);const a={},i=r.createContext(a);function o(e){const n=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/924d6dd6.ee7a4ff8.js b/docs/assets/js/924d6dd6.ee7a4ff8.js
deleted file mode 100644
index 45c0660b19d..00000000000
--- a/docs/assets/js/924d6dd6.ee7a4ff8.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5122],{7665(e,r,n){n.r(r),n.d(r,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>a,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"electron","title":"Seamless Electron Storage with RxDB","description":"Use the RxDB Electron Plugin to share data between main and renderer processes. Enjoy quick queries, real-time sync, and robust offline support.","source":"@site/docs/electron.md","sourceDirName":".","slug":"/electron.html","permalink":"/electron.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Seamless Electron Storage with RxDB","slug":"electron.html","description":"Use the RxDB Electron Plugin to share data between main and renderer processes. Enjoy quick queries, real-time sync, and robust offline support."},"sidebar":"tutorialSidebar","previous":{"title":"Localstorage Meta Optimizer \ud83d\udc51","permalink":"/rx-storage-localstorage-meta-optimizer.html"},"next":{"title":"\u2699\ufe0f Sync Engine","permalink":"/replication.html"}}');var o=n(4848),i=n(8453);const a={title:"Seamless Electron Storage with RxDB",slug:"electron.html",description:"Use the RxDB Electron Plugin to share data between main and renderer processes. Enjoy quick queries, real-time sync, and robust offline support."},t="Electron Plugin",l={},c=[{value:"RxStorage Electron IpcRenderer & IpcMain",id:"rxstorage-electron-ipcrenderer--ipcmain",level:2},{value:"Related",id:"related",level:2}];function d(e){const r={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(r.header,{children:(0,o.jsx)(r.h1,{id:"electron-plugin",children:"Electron Plugin"})}),"\n",(0,o.jsx)(r.h2,{id:"rxstorage-electron-ipcrenderer--ipcmain",children:"RxStorage Electron IpcRenderer & IpcMain"}),"\n",(0,o.jsxs)(r.p,{children:["To use RxDB in ",(0,o.jsx)(r.a,{href:"/electron-database.html",children:"electron"}),", it is recommended to run the RxStorage in the main process and the RxDatabase in the renderer processes. With the rxdb electron plugin you can create a remote RxStorage and consume it from the renderer process."]}),"\n",(0,o.jsxs)(r.p,{children:["To do this in a convenient way, the RxDB electron plugin provides the helper functions ",(0,o.jsx)(r.code,{children:"exposeIpcMainRxStorage"})," and ",(0,o.jsx)(r.code,{children:"getRxStorageIpcRenderer"}),".\nSimilar to the ",(0,o.jsx)(r.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage"}),", these wrap any other ",(0,o.jsx)(r.a,{href:"/rx-storage.html",children:"RxStorage"})," once in the main process and once in each renderer process. In the renderer you can then use the storage to create a ",(0,o.jsx)(r.a,{href:"/rx-database.html",children:"RxDatabase"})," which communicates with the storage of the main process to store and query data."]}),"\n",(0,o.jsx)(r.admonition,{type:"note",children:(0,o.jsxs)(r.p,{children:[(0,o.jsx)(r.code,{children:"nodeIntegration"})," must be enabled in ",(0,o.jsx)(r.a,{href:"https://www.electronjs.org/docs/latest/api/browser-window#new-browserwindowoptions",children:"Electron"}),"."]})}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// main.js"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:"exposeIpcMainRxStorage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'rxdb/plugins/electron'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:"getRxStorageMemory"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'rxdb/plugins/storage-memory'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:".on"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'ready'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" () {"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" exposeIpcMainRxStorage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" key"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'main-storage'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMemory"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" ipcMain"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" electron"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:".ipcMain"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(r.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(r.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(r.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"// renderer.js"})}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:"getRxStorageIpcRenderer"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'rxdb/plugins/electron'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:"getRxStorageMemory"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'rxdb/plugins/storage-memory'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIpcRenderer"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" key"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'main-storage'"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(r.span,{"data-line":"",children:[(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" ipcRenderer"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-constant)"},children:" electron"}),(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:".ipcRenderer"})]}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(r.span,{"data-line":"",children:(0,o.jsx)(r.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"})})]})})}),"\n",(0,o.jsx)(r.h2,{id:"related",children:"Related"}),"\n",(0,o.jsxs)(r.ul,{children:["\n",(0,o.jsx)(r.li,{children:(0,o.jsx)(r.a,{href:"/electron-database.html",children:"Comparison of Electron Databases"})}),"\n"]})]})}function h(e={}){const{wrapper:r}={...(0,i.R)(),...e.components};return r?(0,o.jsx)(r,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453(e,r,n){n.d(r,{R:()=>a,x:()=>t});var s=n(6540);const o={},i=s.createContext(o);function a(e){const r=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function t(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),s.createElement(i.Provider,{value:r},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/92698a99.49ff7265.js b/docs/assets/js/92698a99.49ff7265.js
deleted file mode 100644
index 7408eec9552..00000000000
--- a/docs/assets/js/92698a99.49ff7265.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[4166],{7379(e,s,r){r.r(s),r.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>a,metadata:()=>n,toc:()=>d});const n=JSON.parse('{"id":"rx-storage-indexeddb","title":"Instant Performance with IndexedDB RxStorage","description":"Choose IndexedDB RxStorage for unmatched speed and minimal build size. Perfect for fast-performing apps that demand reliable, lightweight data solutions.","source":"@site/docs/rx-storage-indexeddb.md","sourceDirName":".","slug":"/rx-storage-indexeddb.html","permalink":"/rx-storage-indexeddb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Instant Performance with IndexedDB RxStorage","slug":"rx-storage-indexeddb.html","description":"Choose IndexedDB RxStorage for unmatched speed and minimal build size. Perfect for fast-performing apps that demand reliable, lightweight data solutions."},"sidebar":"tutorialSidebar","previous":{"title":"LocalStorage (Browser)","permalink":"/rx-storage-localstorage.html"},"next":{"title":"OPFS \ud83d\udc51 (Browser)","permalink":"/rx-storage-opfs.html"}}');var o=r(4848),i=r(8453);const a={title:"Instant Performance with IndexedDB RxStorage",slug:"rx-storage-indexeddb.html",description:"Choose IndexedDB RxStorage for unmatched speed and minimal build size. Perfect for fast-performing apps that demand reliable, lightweight data solutions."},t="IndexedDB RxStorage",l={},d=[{value:"IndexedDB performance comparison",id:"indexeddb-performance-comparison",level:2},{value:"Using the IndexedDB RxStorage",id:"using-the-indexeddb-rxstorage",level:2},{value:"Overwrite/Polyfill the native IndexedDB",id:"overwritepolyfill-the-native-indexeddb",level:2},{value:"Storage Buckets",id:"storage-buckets",level:2},{value:"Limitations of the IndexedDB RxStorage",id:"limitations-of-the-indexeddb-rxstorage",level:2}];function c(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"indexeddb-rxstorage",children:"IndexedDB RxStorage"})}),"\n",(0,o.jsxs)(s.p,{children:["The IndexedDB ",(0,o.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," is based on plain IndexedDB and can be used in browsers, ",(0,o.jsx)(s.a,{href:"/electron-database.html",children:"electron"})," or hybrid apps.\nCompared to other browser based storages, the IndexedDB storage has the smallest write- and read latency, the fastest initial page load\nand the smallest build size. Only for big datasets (more than 10k documents), the ",(0,o.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS storage"})," is better suited."]}),"\n",(0,o.jsxs)(s.p,{children:["While the IndexedDB API itself can be very slow, the IndexedDB storage uses many tricks and performance optimizations, some of which are described ",(0,o.jsx)(s.a,{href:"/slow-indexeddb.html",children:"here"}),". For example it uses custom index strings instead of the native IndexedDB indexes, batches cursor for faster bulk reads and many other improvements. The IndexedDB storage also operates on ",(0,o.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Write-ahead_logging",children:"Write-ahead logging"})," similar to SQLite, to improve write latency while still ensuring consistency on writes."]}),"\n",(0,o.jsx)(s.h2,{id:"indexeddb-performance-comparison",children:"IndexedDB performance comparison"}),"\n",(0,o.jsxs)(s.p,{children:["Here is some performance comparison with other storages. Compared to the non-memory storages like ",(0,o.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," and ",(0,o.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"WASM SQLite"}),". IndexedDB has the smallest build size and fastest write speed. Only OPFS is faster on queries over big datasets. See ",(0,o.jsx)(s.a,{href:"/rx-storage-performance.html",children:"performance comparison"})," page for a comparison with all storages."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/rx-storage-performance-browser.png",alt:"IndexedDB performance",width:"700"})}),"\n",(0,o.jsx)(s.h2,{id:"using-the-indexeddb-rxstorage",children:"Using the IndexedDB RxStorage"}),"\n",(0,o.jsxs)(s.p,{children:["To use the indexedDB storage you import it from the ",(0,o.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"})," npm module and use ",(0,o.jsx)(s.code,{children:"getRxStorageIndexedDB()"})," when creating the ",(0,o.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"}),"."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * For better performance, queries run with a batched cursor."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * You can change the batchSize to optimize the query time"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * for specific queries."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * You should only change this value when you are also doing performance measurements."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=300]"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 300"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"overwritepolyfill-the-native-indexeddb",children:"Overwrite/Polyfill the native IndexedDB"}),"\n",(0,o.jsxs)(s.p,{children:["Node.js has no IndexedDB API. To still run the IndexedDB ",(0,o.jsx)(s.code,{children:"RxStorage"})," in Node.js, for example to run unit tests, you have to polyfill it.\nYou can do that by using the ",(0,o.jsx)(s.a,{href:"https://github.com/dumbmatter/fakeIndexedDB",children:"fake-indexeddb"})," module and pass it to the ",(0,o.jsx)(s.code,{children:"getRxStorageIndexedDB()"})," function."]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"//> npm install fake-indexeddb --save"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" fakeIndexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'fake-indexeddb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" fakeIDBKeyRange"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" require"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'fake-indexeddb/lib/FDBKeyRange'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" fakeIndexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" IDBKeyRange"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" fakeIDBKeyRange"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "})]})})}),"\n",(0,o.jsx)(s.h2,{id:"storage-buckets",children:"Storage Buckets"}),"\n",(0,o.jsxs)(s.p,{children:["The ",(0,o.jsx)(s.a,{href:"https://wicg.github.io/storage-buckets/",children:"Storage Buckets API"}),' provides a way for sites to organize locally stored data into groupings called "storage buckets". This allows the user agent or sites to manage and delete buckets independently rather than applying the same treatment to all the data from a single origin. ',(0,o.jsx)(s.a,{href:"https://developer.chrome.com/docs/web-platform/storage-buckets?hl=en",children:"Read More"})]}),"\n",(0,o.jsxs)(s.p,{children:["To use different storage buckets with the RxDB IndexedDB Storage, you can use a function instead of a plain object when providing the ",(0,o.jsx)(s.code,{children:"indexedDB"})," attribute:"]}),"\n",(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'exampledb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" indexedDB"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(params) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myStorageBucket"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" navigator"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"storageBuckets"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".open"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'myApp-'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" params"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".databaseName);"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myStorageBucket"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".indexedDB;"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" IDBKeyRange"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(s.h2,{id:"limitations-of-the-indexeddb-rxstorage",children:"Limitations of the IndexedDB RxStorage"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsxs)(s.li,{children:["It is part of the ",(0,o.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"})," plugin that must be purchased. If you just need a storage that works in the browser and you do not have to care about performance, you can use the ",(0,o.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage storage"})," instead."]}),"\n",(0,o.jsxs)(s.li,{children:["The IndexedDB storage requires support for ",(0,o.jsx)(s.a,{href:"https://caniuse.com/indexeddb2",children:"IndexedDB v2"}),", it does not work on Internet Explorer."]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(c,{...e})}):c(e)}},8453(e,s,r){r.d(s,{R:()=>a,x:()=>t});var n=r(6540);const o={},i=n.createContext(o);function a(e){const s=n.useContext(i);return n.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),n.createElement(i.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/9293.098c020a.js b/docs/assets/js/9293.098c020a.js
deleted file mode 100644
index 7f0327c62a9..00000000000
--- a/docs/assets/js/9293.098c020a.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9293],{5955(e,t,i){i.d(t,{A:()=>h});i(6540);var s=i(4164),n=i(1312),r=i(1107),l=i(4848);function h({className:e}){return(0,l.jsx)("main",{className:(0,s.A)("container margin-vert--xl",e),children:(0,l.jsx)("div",{className:"row",children:(0,l.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,l.jsxs)(r.A,{as:"h1",className:"hero__title",children:[(0,l.jsx)("a",{href:"/",children:(0,l.jsx)("div",{style:{textAlign:"center"},children:(0,l.jsx)("img",{src:"https://rxdb.info/files/logo/rxdb_javascript_database.svg",alt:"RxDB",width:"160"})})}),(0,l.jsx)(n.A,{id:"theme.NotFound.title",description:"The title of the 404 page",children:"404 Page Not Found"})]}),(0,l.jsx)("p",{children:(0,l.jsx)(n.A,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page",children:"The page you are looking for does not exist anymore or never has existed. If you have found this page through a link, you should tell the link author to update it."})}),(0,l.jsx)("p",{children:"Maybe one of these can help you to find the desired content:"}),(0,l.jsx)("div",{className:"ul-container",children:(0,l.jsxs)("ul",{children:[(0,l.jsx)("li",{children:(0,l.jsx)("a",{href:"https://rxdb.info/overview.html",children:"RxDB Documentation"})}),(0,l.jsx)("li",{children:(0,l.jsx)("a",{href:"/chat/",children:"RxDB Discord Channel"})}),(0,l.jsx)("li",{children:(0,l.jsx)("a",{href:"https://twitter.com/intent/user?screen_name=rxdbjs",children:"RxDB on twitter"})}),(0,l.jsx)("li",{children:(0,l.jsx)("a",{href:"/code/",children:"RxDB at Github"})})]})})]})})})}},9293(e,t,i){i.r(t),i.d(t,{default:()=>a});i(6540);var s=i(1312),n=i(5500),r=i(8711),l=i(5955),h=i(4848);function a(){const e=(0,s.T)({id:"theme.NotFound.title",message:"RxDB - 404 Page Not Found"});return(0,h.jsxs)(h.Fragment,{children:[(0,h.jsx)(n.be,{title:e}),(0,h.jsx)(r.A,{title:"RxDB - 404 Page Not Found",children:(0,h.jsx)(l.A,{})})]})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/931f4566.40f8ed4e.js b/docs/assets/js/931f4566.40f8ed4e.js
deleted file mode 100644
index aae8f38e701..00000000000
--- a/docs/assets/js/931f4566.40f8ed4e.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3595],{8288(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"schema-validation","title":"Schema Validation","description":"RxDB has multiple validation implementations that can be used to ensure that your document data is always matching the provided JSON","source":"@site/docs/schema-validation.md","sourceDirName":".","slug":"/schema-validation.html","permalink":"/schema-validation.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Schema Validation","slug":"schema-validation.html"},"sidebar":"tutorialSidebar","previous":{"title":"FoundationDB","permalink":"/rx-storage-foundationdb.html"},"next":{"title":"Encryption","permalink":"/encryption.html"}}');var i=n(4848),a=n(8453);const o={title:"Schema Validation",slug:"schema-validation.html"},t="Schema validation",l={},d=[{value:"validate-ajv",id:"validate-ajv",level:3},{value:"validate-z-schema",id:"validate-z-schema",level:3},{value:"validate-is-my-json-valid",id:"validate-is-my-json-valid",level:3},{value:"Custom Formats",id:"custom-formats",level:2},{value:"Ajv Custom Format",id:"ajv-custom-format",level:3},{value:"Z-Schema Custom Format",id:"z-schema-custom-format",level:3},{value:"Performance comparison of the validators",id:"performance-comparison-of-the-validators",level:2}];function c(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"schema-validation",children:"Schema validation"})}),"\n",(0,i.jsxs)(s.p,{children:["RxDB has multiple validation implementations that can be used to ensure that your document data is always matching the provided JSON\nschema of your ",(0,i.jsx)(s.code,{children:"RxCollection"}),"."]}),"\n",(0,i.jsxs)(s.p,{children:["The schema validation is ",(0,i.jsx)(s.strong,{children:"not a plugin"})," but comes in as a wrapper around any other ",(0,i.jsx)(s.code,{children:"RxStorage"})," and it will then validate all data that is written into that storage. This is required for multiple reasons:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["It allows us to run the validation inside of a ",(0,i.jsx)(s.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage"})," instead of running it in the main JavaScript process."]}),"\n",(0,i.jsxs)(s.li,{children:["It allows us to configure which ",(0,i.jsx)(s.code,{children:"RxDatabase"})," instance must use the validation and which does not. In production it often makes sense to validate user data, but you might not need the validation for data that is only replicated from the backend."]}),"\n"]}),"\n",(0,i.jsx)(s.admonition,{type:"warning",children:(0,i.jsxs)(s.p,{children:["Schema validation can be ",(0,i.jsx)(s.strong,{children:"CPU expensive"})," and increases your build size. You should always use a schema validation in development mode. For most use cases, you ",(0,i.jsx)(s.strong,{children:"should not"})," use a validation in production for better performance."]})}),"\n",(0,i.jsxs)(s.p,{children:["When no validation is used, any document data can be saved but there might be ",(0,i.jsx)(s.strong,{children:"undefined behavior"})," when saving data that does not comply to the schema of a ",(0,i.jsx)(s.code,{children:"RxCollection"}),"."]}),"\n",(0,i.jsxs)(s.p,{children:["RxDB has different implementations to validate data, each of them is based on a different ",(0,i.jsx)(s.a,{href:"https://json-schema.org/tools",children:"JSON Schema library"}),". In this example we use the ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"}),", but you can wrap the validation around ",(0,i.jsx)(s.strong,{children:"any other"})," ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"}),"."]}),"\n",(0,i.jsx)(s.h3,{id:"validate-ajv",children:"validate-ajv"}),"\n",(0,i.jsxs)(s.p,{children:["A validation-module that does the schema-validation. This one is using ",(0,i.jsx)(s.a,{href:"https://github.com/epoberezkin/ajv",children:"ajv"})," as validator which is a bit faster. Better compliant to the jsonschema-standard but also has a bigger build-size."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedValidateAjvStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/validate-ajv'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap the validation around the main RxStorage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedValidateAjvStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" randomCouchString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"validate-z-schema",children:"validate-z-schema"}),"\n",(0,i.jsxs)(s.p,{children:["Both ",(0,i.jsx)(s.code,{children:"is-my-json-valid"})," and ",(0,i.jsx)(s.code,{children:"validate-ajv"})," use ",(0,i.jsx)(s.code,{children:"eval()"})," to perform validation which might not be wanted when ",(0,i.jsx)(s.code,{children:"'unsafe-eval'"})," is not allowed in Content Security Policies. This one is using ",(0,i.jsx)(s.a,{href:"https://github.com/zaggino/z-schema",children:"z-schema"})," as validator which doesn't use ",(0,i.jsx)(s.code,{children:"eval"}),"."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedValidateZSchemaStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/validate-z-schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap the validation around the main RxStorage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedValidateZSchemaStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" randomCouchString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"validate-is-my-json-valid",children:"validate-is-my-json-valid"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"WARNING"}),": The ",(0,i.jsx)(s.code,{children:"is-my-json-valid"})," validation is no longer supported until ",(0,i.jsx)(s.a,{href:"https://github.com/mafintosh/is-my-json-valid/pull/192",children:"this bug"})," is fixed."]}),"\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.code,{children:"validate-is-my-json-valid"})," plugin uses ",(0,i.jsx)(s.a,{href:"https://www.npmjs.com/package/is-my-json-valid",children:"is-my-json-valid"})," for schema validation."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedValidateIsMyJsonValidStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/validate-is-my-json-valid'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap the validation around the main RxStorage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedValidateIsMyJsonValidStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" randomCouchString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"custom-formats",children:"Custom Formats"}),"\n",(0,i.jsxs)(s.p,{children:["The schema validators provide methods to add custom formats like a ",(0,i.jsx)(s.code,{children:"email"})," format.\nYou have to add these formats ",(0,i.jsx)(s.strong,{children:"before"})," you create your database."]}),"\n",(0,i.jsx)(s.h3,{id:"ajv-custom-format",children:"Ajv Custom Format"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getAjv } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/validate-ajv'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" ajv"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getAjv"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"ajv"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addFormat"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'email'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" validate"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" v "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" v"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".includes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'@'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// ensure email fields contain the @ symbol"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h3,{id:"z-schema-custom-format",children:"Z-Schema Custom Format"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { ZSchemaClass } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/validate-z-schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"ZSchemaClass"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".registerFormat"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'email'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (v"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" v"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".includes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'@'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"); "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// ensure email fields contain the @ symbol"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"performance-comparison-of-the-validators",children:"Performance comparison of the validators"}),"\n",(0,i.jsxs)(s.p,{children:["The RxDB team ran performance benchmarks using two storage options on an Ubuntu 24.04 machine with Chrome version ",(0,i.jsx)(s.code,{children:"131.0.6778.85"}),". The testing machine has 32 core ",(0,i.jsx)(s.code,{children:"13th Gen Intel(R) Core(TM) i9-13900HX"})," CPU."]}),"\n",(0,i.jsx)(s.p,{children:"IndexedDB Storage (based on the IndexedDB API in the browser):"}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:(0,i.jsx)(s.strong,{children:"IndexedDB Storage"})}),(0,i.jsx)(s.th,{style:{textAlign:"center"},children:"Time to First insert"}),(0,i.jsx)(s.th,{style:{textAlign:"right"},children:"Insert 3000 documents"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"no validator"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"68 ms"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"213 ms"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"ajv"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"67 ms"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"216 ms"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"z-schema"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"71 ms"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"230 ms"})]})]})]}),"\n",(0,i.jsx)(s.p,{children:"Memory Storage: stores everything in memory for extremely fast reads and writes, with no persistence by default. Often used with the RxDB memory-mapped plugin that processes data in memory an later persists to disc in background:"}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:(0,i.jsx)(s.strong,{children:"Memory Storage"})}),(0,i.jsx)(s.th,{style:{textAlign:"center"},children:"Time to First insert"}),(0,i.jsx)(s.th,{style:{textAlign:"right"},children:"Insert 3000 documents"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"no validator"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"1.15 ms"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"0.8 ms"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"ajv"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"3.05 ms"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"2.7 ms"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"z-schema"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"0.9 ms"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"18 ms"})]})]})]}),"\n",(0,i.jsx)(s.p,{children:"Including a validator library also increases your JavaScript bundle size. Here's how it breaks down (minified + gzip):"}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsxs)(s.th,{children:[(0,i.jsx)(s.strong,{children:"Build Size"})," (minified+gzip)"]}),(0,i.jsx)(s.th,{style:{textAlign:"center"},children:"Build Size (IndexedDB)"}),(0,i.jsx)(s.th,{style:{textAlign:"right"},children:"Build Size (memory)"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"no validator"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"73103 B"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"39976 B"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"ajv"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"106135 B"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"72773 B"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"z-schema"}),(0,i.jsx)(s.td,{style:{textAlign:"center"},children:"125186 B"}),(0,i.jsx)(s.td,{style:{textAlign:"right"},children:"91882 B"})]})]})]})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}},8453(e,s,n){n.d(s,{R:()=>o,x:()=>t});var r=n(6540);const i={},a=r.createContext(i);function o(e){const s=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),r.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/951d0efb.94a862cc.js b/docs/assets/js/951d0efb.94a862cc.js
deleted file mode 100644
index 641b617da53..00000000000
--- a/docs/assets/js/951d0efb.94a862cc.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6115],{3247(e,n,s){s.d(n,{g:()=>i});var r=s(4848);function i(e){const n=[];let s=null;return e.children.forEach(e=>{e.props.id?(s&&n.push(s),s={headline:e,paragraphs:[]}):s&&s.paragraphs.push(e)}),s&&n.push(s),(0,r.jsx)("div",{style:o.stepsContainer,children:n.map((e,n)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:n+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,n)=>(0,r.jsx)("div",{style:o.item,children:e},n))]})]},n))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5160(e,n,s){s.r(n),s.d(n,{assets:()=>p,contentTitle:()=>h,default:()=>y,frontMatter:()=>d,metadata:()=>r,toc:()=>x});const r=JSON.parse('{"id":"replication-mongodb","title":"MongoDB Realtime Sync Engine for Local-First Apps","description":"Build real-time, offline-capable apps with RxDB + MongoDB replication. Push/pull changes, use change streams, and keep data in sync across devices.","source":"@site/docs/replication-mongodb.md","sourceDirName":".","slug":"/replication-mongodb.html","permalink":"/replication-mongodb.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"MongoDB Realtime Sync Engine for Local-First Apps","slug":"replication-mongodb.html","description":"Build real-time, offline-capable apps with RxDB + MongoDB replication. Push/pull changes, use change streams, and keep data in sync across devices."},"sidebar":"tutorialSidebar","previous":{"title":"Firestore Replication","permalink":"/replication-firestore.html"},"next":{"title":"Supabase Replication","permalink":"/replication-supabase.html"}}');var i=s(4848),o=s(8453),l=s(2636),a=s(3247),t=s(2271),c=s(7482);const d={title:"MongoDB Realtime Sync Engine for Local-First Apps",slug:"replication-mongodb.html",description:"Build real-time, offline-capable apps with RxDB + MongoDB replication. Push/pull changes, use change streams, and keep data in sync across devices."},h="MongoDB Replication Plugin for RxDB - Real-Time, Offline-First Sync",p={},x=[{value:"Key Features",id:"key-features",level:2},{value:"Architecture Overview",id:"architecture-overview",level:2},{value:"Setting up the Client-RxServer-MongoDB Sync",id:"setting-up-the-client-rxserver-mongodb-sync",level:2},{value:"Install the Client Dependencies",id:"install-the-client-dependencies",level:3},{value:"Set up a MongoDB Server",id:"set-up-a-mongodb-server",level:3},{value:"Shell",id:"shell",level:3},{value:"Docker",id:"docker",level:3},{value:"MongoDB Atlas",id:"mongodb-atlas",level:3},{value:"Create a MongoDB Database and Collection",id:"create-a-mongodb-database-and-collection",level:3},{value:"Create a RxDB Database and Collection",id:"create-a-rxdb-database-and-collection",level:3},{value:"Sync the Collection with the MongoDB Server",id:"sync-the-collection-with-the-mongodb-server",level:3},{value:"Start a RxServer",id:"start-a-rxserver",level:3},{value:"Sync a Client with the RxServer",id:"sync-a-client-with-the-rxserver",level:3},{value:"Follow Up",id:"follow-up",level:2}];function k(e){const n={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"mongodb-replication-plugin-for-rxdb---real-time-offline-first-sync",children:"MongoDB Replication Plugin for RxDB - Real-Time, Offline-First Sync"})}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"./files/icons/mongodb.svg",alt:"MongoDB Sync",height:"60",class:"img-padding img-in-text-right"})}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.a,{href:"https://www.mongodb.com/",children:"MongoDB"})," Replication Plugin for RxDB delivers seamless, two-way synchronization between MongoDB and RxDB, enabling ",(0,i.jsx)(n.a,{href:"/articles/realtime-database.html",children:"real-time"})," updates and ",(0,i.jsx)(n.a,{href:"/offline-first.html",children:"offline-first"})," functionality for your applications. Built on ",(0,i.jsx)(n.strong,{children:"MongoDB Change Streams"}),", it supports both Atlas and self-hosted deployments, ensuring your data stays consistent across every device and service."]}),"\n",(0,i.jsxs)(n.p,{children:["Behind the scenes, the plugin is powered by the RxDB ",(0,i.jsx)(n.a,{href:"/replication.html",children:"Sync Engine"}),", which manages the complexities of real-world data replication for you. It automatically handles ",(0,i.jsx)(n.a,{href:"/transactions-conflicts-revisions.html",children:"conflict detection and resolution"}),", maintains precise checkpoints for incremental updates, and gracefully manages transitions between offline and online states. This means you don't need to manually implement retry logic, reconcile divergent changes, or worry about data loss during connectivity drops, the Sync Engine ensures consistency and reliability in every sync cycle."]}),"\n",(0,i.jsx)(n.h2,{id:"key-features",children:"Key Features"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Two-way replication"})," between MongoDB and RxDB collections"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Offline-first support"})," with automatic incremental re-sync"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Incremental updates"})," via MongoDB Change Streams"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Conflict resolution"})," handled by the RxDB Sync Engine"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Atlas and self-hosted support"})," for replica sets and sharded clusters"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"architecture-overview",children:"Architecture Overview"}),"\n",(0,i.jsxs)(n.p,{children:["The plugin operates in a three-tier architecture: Clients connect to ",(0,i.jsx)(n.a,{href:"/rx-server.html",children:"RxServer"}),", which in turn connects to MongoDB. RxServer streams changes from MongoDB to connected clients and pushes client-side updates back to MongoDB."]}),"\n",(0,i.jsxs)(n.p,{children:["For the client side, RxServer exposes a ",(0,i.jsx)(n.a,{href:"/rx-server.html#replication-endpoint",children:"replication endpoint"})," over WebSocket or HTTP, which your RxDB-powered applications can consume."]}),"\n",(0,i.jsx)(n.p,{children:"The following diagram illustrates the flow of updates between clients, RxServer, and MongoDB in a live synchronization setup:"}),"\n",(0,i.jsx)(c.u,{dbIcon:"/files/icons/mongodb-icon.svg",dbLabel:"MongoDB"}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsx)("br",{}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:"The MongoDB Replication Plugin is optimized for Node.js environments (e.g., when RxDB runs within RxServer or other backend services). Direct connections from browsers or mobile apps to MongoDB are not supported because MongoDB does not use HTTP as its wire protocol and requires a driver-level connection to a replica set or sharded cluster."})}),"\n",(0,i.jsx)(n.h2,{id:"setting-up-the-client-rxserver-mongodb-sync",children:"Setting up the Client-RxServer-MongoDB Sync"}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(n.h3,{id:"install-the-client-dependencies",children:"Install the Client Dependencies"}),(0,i.jsx)(n.p,{children:"In your JavaScript project, install the RxDB libraries and the MongoDB node.js driver:"}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.code,{children:"npm install rxdb rxdb-server mongodb --save"})}),(0,i.jsx)(n.h3,{id:"set-up-a-mongodb-server",children:"Set up a MongoDB Server"}),(0,i.jsxs)(n.p,{children:["As first step, you need access to a running MongoDB Server. This can be done by either running a server locally or using the Atlas Cloud. Notice that we need to have a ",(0,i.jsx)(n.a,{href:"https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set/",children:"replica set"})," because only on these, the MongoDB changestream can be used."]}),(0,i.jsxs)(l.t,{children:[(0,i.jsx)(n.h3,{id:"shell",children:"Shell"}),(0,i.jsx)(n.p,{children:"If you have installed MongoDB locally, you can start the server with this command:"}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.code,{children:"mongod --replSet rs0 --bind_ip_all"})}),(0,i.jsx)(n.h3,{id:"docker",children:"Docker"}),(0,i.jsx)(n.p,{children:"If you have docker installed, you can start a container that runs the MongoDB server:"}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.code,{children:"docker run -p 27017:27017 -p 27018:27018 -p 27019:27019 --rm --name rxdb-mongodb mongo:8.0.4 mongod --replSet rs0 --bind_ip_all"})}),(0,i.jsx)(n.h3,{id:"mongodb-atlas",children:"MongoDB Atlas"}),(0,i.jsx)(n.p,{children:"Learn here how to create a MongoDB atlas account and how to start a MongoDB cluster that runs in the cloud:"}),(0,i.jsx)("br",{}),(0,i.jsx)("center",{children:(0,i.jsx)(t.N,{videoId:"bBA9rUdqmgY",title:"Create MongoDB Atlas Server",duration:"19:55"})})]}),(0,i.jsx)("br",{}),(0,i.jsxs)(n.p,{children:["After this step you should have a valid connection string that points to a running MongoDB Server like ",(0,i.jsx)(n.code,{children:"mongodb://localhost:27017/"}),"."]}),(0,i.jsx)(n.h3,{id:"create-a-mongodb-database-and-collection",children:"Create a MongoDB Database and Collection"}),(0,i.jsx)(n.p,{children:"On your MongoDB server, make sure to create a database and a collection."}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> server.ts"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { MongoClient } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mongodb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoClient"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" MongoClient"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'mongodb://localhost:27017/?directConnection=true'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoClient"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'my-database'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".createCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'my-collection'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" changeStreamPreAndPostImages"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { enabled"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["To observe document deletions on the changestream, ",(0,i.jsx)(n.code,{children:"changeStreamPreAndPostImages"})," must be enabled. This is not required if you have an insert/update-only collection where no documents are deleted ever."]})}),(0,i.jsx)(n.h3,{id:"create-a-rxdb-database-and-collection",children:"Create a RxDB Database and Collection"}),(0,i.jsxs)(n.p,{children:["Now we create an RxDB ",(0,i.jsx)(n.a,{href:"/rx-database.html",children:"database"})," and a ",(0,i.jsx)(n.a,{href:"/rx-collection.html",children:"collection"}),". In this example the ",(0,i.jsx)(n.a,{href:"/rx-storage-memory.html",children:"memory storage"}),", in production you would use a ",(0,i.jsx)(n.a,{href:"/rx-storage.html",children:"persistent storage"})," instead."]}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> server.ts"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" addRxPlugin } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageMemory } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-memory'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Create server-side RxDB instance"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'serverdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageMemory"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Add your collection schema"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'passportId'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" passportId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'passportId'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'lastName'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,i.jsx)(n.h3,{id:"sync-the-collection-with-the-mongodb-server",children:"Sync the Collection with the MongoDB Server"}),(0,i.jsxs)(n.p,{children:["Now we can start a ",(0,i.jsx)(n.a,{href:"/replication.html",children:"replication"})," that does a two-way replication between the RxDB Collection and the MongoDB Collection."]}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> server.ts"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateMongoDB } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-mongodb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateMongoDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mongodb"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collectionName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-collection'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" connection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mongodb://localhost:27017'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" databaseName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-database'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".humans"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'humans-mongodb-sync'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "})]})})}),(0,i.jsx)(n.admonition,{title:"You can do many things with the replication state",type:"note",children:(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"RxMongoDBReplicationState"})," which is returned from ",(0,i.jsx)(n.code,{children:"replicateMongoDB()"})," allows you to run all functionality of the normal ",(0,i.jsx)(n.a,{href:"/replication.html",children:"RxReplicationState"})," like observing errors or doing start/stop operations."]})}),(0,i.jsx)(n.h3,{id:"start-a-rxserver",children:"Start a RxServer"}),(0,i.jsxs)(n.p,{children:["Now that we have a RxDatabase and Collection that is replicated with MongoDB, we can spawn a ",(0,i.jsx)(n.a,{href:"/rx-server.html",children:"RxServer"})," on top of it. This server can then be used by client devices to connect."]}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> server.ts"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxServer } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-server/plugins/server'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { RxServerAdapterExpress } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-server/plugins/adapter-express'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" server"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxServer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" adapter"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" RxServerAdapterExpress"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" port"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 8080"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" cors"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '*'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" endpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" server"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addReplicationEndpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'humans'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".humans"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Replication endpoint:'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `http://localhost:8080/"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"endpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".urlPath"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// do not forget to start the server!"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" server"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".start"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),(0,i.jsx)(n.h3,{id:"sync-a-client-with-the-rxserver",children:"Sync a Client with the RxServer"}),(0,i.jsx)(n.p,{children:"On the client-side we create the exact same RxDatabase and collection and then replicate it with the replication endpoint of the RxServer."}),(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"//> client.ts"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageDexie } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-dexie'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateServer } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-server/plugins/replication-server'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb-client'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageDexie"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" "})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'passportId'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" passportId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" firstName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'passportId'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'firstName'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'lastName'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// Start replication to the RxServer endpoint printed by the server:"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// e.g. http://localhost:8080/humans/0"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateServer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'humans-rxserver'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".humans"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://localhost:8080/humans/0'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "})]})})})]}),"\n",(0,i.jsx)(n.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Try it out with the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb-mongodb-sync-example",children:"RxDB-MongoDB example repository"})]}),"\n",(0,i.jsxs)(n.li,{children:["Read ",(0,i.jsx)(n.a,{href:"https://www.mongodb.com/company/blog/innovation/from-local-global-scalable-edge-apps-rxdb",children:"From Local to Global: Scalable Edge Apps with RxDB + MongoDB"})]}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"/replication.html",children:"Replication API Reference"})}),"\n",(0,i.jsx)(n.li,{children:(0,i.jsx)(n.a,{href:"/rx-server.html",children:"RxServer Documentation"})}),"\n",(0,i.jsxs)(n.li,{children:["Join our ",(0,i.jsx)(n.a,{href:"./chat",children:"Discord Forum"})," for questions and feedback"]}),"\n"]})]})}function y(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(k,{...e})}):k(e)}},7482(e,n,s){s.d(n,{u:()=>i});s(6540);var r=s(4848);function i({className:e="",style:n,clientLabels:s=["Client A","Client B","Client C"],serverLabel:i="RxServer",dbLabel:o="Backend",dbIcon:l="",showServer:a=!0}){const t="/files/logo/logo.svg",c=a?{}:{gridColumn:"2 / 5",width:"90%",marginLeft:"5%",marginRight:0};return(0,r.jsxs)("div",{className:`rxdb-diagram ${e}`,style:n,children:[(0,r.jsx)("style",{children:'\n .rxdb-diagram {\n padding-top: 20px;\n padding-bottom: 20px;\n box-sizing: border-box;\n color: inherit;\n width: 100%;\n max-width: 100%;\n overflow: hidden;\n margin: 0 auto;\n font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI",\n Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";\n }\n\n /* Stage keeps fixed aspect ratio and scales */\n .rxdb-diagram .stage {\n width: 100%;\n aspect-ratio: 816 / 336;\n position: relative;\n }\n\n /* Grid fills the stage completely */\n .rxdb-diagram .grid {\n position: absolute;\n inset: 0;\n display: grid;\n grid-template-columns:\n 26.960784% 5.882353% 31.862745% 5.882353% 29.411765%;\n grid-template-rows:\n 23.809524% 14.285714% 23.809524% 14.285714% 23.809524%;\n align-items: center;\n justify-items: stretch;\n }\n\n .rxdb-diagram {\n --stroke: clamp(1px, 0.35vmin, 2px);\n --radius: clamp(8px, 1.2vmin, 14px);\n --pad: clamp(8px, 1.2vmin, 18px);\n --line: currentColor;\n }\n\n .rxdb-diagram .logo {\n height: 1.2em;\n width: auto;\n display: inline-block;\n object-fit: contain;\n margin-right: 10px;\n }\n\n .rxdb-diagram .box {\n border: var(--stroke) dashed var(--line);\n border-radius: var(--radius);\n background: transparent;\n display: flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n font-weight: 600;\n padding: var(--pad);\n line-height: 1.2;\n user-select: none;\n }\n\n /* Server and DB take full height of stage */\n .rxdb-diagram .server,\n .rxdb-diagram .db {\n grid-row: 1 / -1;\n height: 100%;\n }\n\n /* Horizontal arrows */\n .rxdb-diagram .arrow {\n position: relative;\n height: 0;\n border-top: var(--stroke) solid var(--line);\n width: calc(100% - 20%);\n margin-left: 10%;\n margin-right: -1.5%;\n }\n\n .rxdb-diagram .arrow::before,\n .rxdb-diagram .arrow::after {\n content: "";\n position: absolute;\n top: 50%;\n width: clamp(6px, 1.2vmin, 10px);\n height: clamp(6px, 1.2vmin, 10px);\n border-top: var(--stroke) solid var(--line);\n border-right: var(--stroke) solid var(--line);\n transform-origin: center;\n }\n .rxdb-diagram .arrow::before {\n left: 0;\n transform: translate(-0%, -60%) rotate(-135deg);\n }\n .rxdb-diagram .arrow::after {\n right: 0;\n transform: translate(0%, -60%) rotate(45deg);\n }\n '}),(0,r.jsx)("div",{className:"stage","aria-label":a?"Clients to RxServer to MongoDB diagram":"Clients to MongoDB diagram",children:(0,r.jsxs)("div",{className:"grid",children:[(0,r.jsxs)("div",{className:"box",style:{gridColumn:1,gridRow:1},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),s[0]||"Client A"]}),(0,r.jsx)("div",{className:"arrow",style:{gridColumn:a?2:c.gridColumn,gridRow:1,...a?{}:{width:"90%",marginLeft:"5%",marginRight:0}},"aria-hidden":!0}),a&&(0,r.jsxs)("div",{className:"box server",style:{gridColumn:3},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),i]}),a&&(0,r.jsx)("div",{className:"arrow",style:{gridColumn:4,gridRow:3},"aria-hidden":!0}),(0,r.jsxs)("div",{className:"box db",style:{gridColumn:5},children:[(0,r.jsx)("img",{src:l,alt:"",className:"logo","aria-hidden":!0}),o]}),(0,r.jsxs)("div",{className:"box",style:{gridColumn:1,gridRow:3},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),s[1]||"Client B"]}),(0,r.jsx)("div",{className:"arrow",style:{gridColumn:a?2:c.gridColumn,gridRow:3,...a?{}:{width:"90%",marginLeft:"5%",marginRight:0}},"aria-hidden":!0}),(0,r.jsxs)("div",{className:"box",style:{gridColumn:1,gridRow:5},children:[(0,r.jsx)("img",{src:t,alt:"",className:"logo","aria-hidden":!0}),s[2]||"Client C"]}),(0,r.jsx)("div",{className:"arrow",style:{gridColumn:a?2:c.gridColumn,gridRow:5,...a?{}:{width:"90%",marginLeft:"5%",marginRight:0}},"aria-hidden":!0})]})})]})}},8453(e,n,s){s.d(n,{R:()=>l,x:()=>a});var r=s(6540);const i={},o=r.createContext(i);function l(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:l(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/98405524.b9ffa436.js b/docs/assets/js/98405524.b9ffa436.js
deleted file mode 100644
index 9c02c568d53..00000000000
--- a/docs/assets/js/98405524.b9ffa436.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5504],{1934(e,s,r){r.r(s),r.d(s,{default:()=>n});var i=r(4586),t=r(8711),l=(r(6540),r(4848));function n(){const{siteConfig:e}=(0,i.A)();return(0,l.jsx)(t.A,{title:`RxDB User Survey - ${e.title}`,description:"RxDB User Survey",children:(0,l.jsx)("main",{children:(0,l.jsxs)("div",{className:"redirectBox",style:{textAlign:"center"},children:[(0,l.jsx)("a",{href:"/",children:(0,l.jsx)("div",{className:"logo",children:(0,l.jsx)("img",{src:"/files/logo/logo_text.svg",alt:"RxDB",width:160})})}),(0,l.jsx)("h1",{children:"RxDB User Survey"}),(0,l.jsx)("p",{children:(0,l.jsx)("b",{children:"You will be redirected in a few seconds."})}),(0,l.jsx)("p",{children:(0,l.jsx)("a",{href:"https://forms.gle/pe8vxaXez1A6X95EA",children:"Click here to open the Survey"})}),(0,l.jsx)("meta",{httpEquiv:"Refresh",content:"0; url=https://forms.gle/pe8vxaXez1A6X95EA"})]})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/9850.34d66cab.js b/docs/assets/js/9850.34d66cab.js
deleted file mode 100644
index 963b66bc4a3..00000000000
--- a/docs/assets/js/9850.34d66cab.js
+++ /dev/null
@@ -1 +0,0 @@
-(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9850],{175(e,t,n){"use strict";n.d(t,{q:()=>i});var r=n(2235),o=n(3587);const i=(e,t,n)=>{(0,r.vA)((0,r.cy)(t),"Invalid expression. $nor expects value to be an array.");const i=(0,o.s)("$or",t,n);return e=>!i(e)}},235(e,t,n){"use strict";n.d(t,{R0:()=>u,lI:()=>i});var r=n(1693);function o(e){return e[e.length-1]}function i(e){return(t=o(e))&&(0,r.T)(t.schedule)?e.pop():void 0;var t}function u(e,t){return"number"==typeof o(e)?e.pop():t}},558(e,t,n){"use strict";n.d(t,{N:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.NV)},695(e,t,n){"use strict";n.d(t,{C:()=>u});var r=n(4953),o=n(2235);function i(e,t,n=!0){const r={exclusions:[],inclusions:[],positional:0},u=Object.keys(e);assert(u.length,"Invalid empty sub-projection");const s=t?.idKey;let a=!1;for(const c of u){if(c.startsWith("$"))return assert(!n&&1===u.length,`FieldPath field names may not start with '$', given '${c}'.`),r;c.endsWith(".$")&&r.positional++;const l=e[c];if(!1===l||(0,o.Et)(l)&&0===l)c===s?a=!0:r.exclusions.push(c);else if((0,o.Gv)(l)){const e=i(l,t,!1);e.inclusions.length||e.exclusions.length?(e.inclusions.forEach(e=>r.inclusions.push(`${c}.${e}`)),e.exclusions.forEach(e=>r.exclusions.push(`${c}.${e}`))):r.inclusions.includes(c)||r.inclusions.push(c),r.positional+=e.positional}else r.inclusions.push(c);assert(!(r.exclusions.length&&r.inclusions.length),"Cannot do exclusion and inclusion in projection."),assert(r.positional<=1,"Cannot specify more than one positional projection.")}if(a&&r.exclusions.push(s),n){const e=new o.yk;r.exclusions.forEach(t=>assert(e.add(t),`Path collision at ${t}.`)),r.inclusions.forEach(t=>assert(e.add(t),`Path collision at ${t}.`)),r.exclusions.sort(),r.inclusions.sort()}return r}const u=(e,t,n)=>{if((0,o.Im)(t))return e;const u=i(t,n),s=function(e,t,n){const i=t.idKey,{exclusions:u,inclusions:s}=n,a={},c={preserveMissing:!0};for(const r of u)a[r]=(e,t)=>{(0,o.yT)(e,r,{descendArray:!0})};for(const v of s){const n=(0,o.hd)(e,v)??e[v];if(v.endsWith(".$")&&1===n){const e=t?.local?.condition;(0,o.vA)(e,"positional operator '.$' couldn't find matching element in the array.");const n=v.slice(0,-2);a[n]=l(n,e,t);continue}if((0,o.cy)(n))a[v]=(e,i)=>{t.update({root:i});const u=n.map(e=>(0,r.px)(i,e,null,t)??null);(0,o.KY)(e,v,u)};else if((0,o.Et)(n)||!0===n)a[v]=(e,n)=>{t.update({root:n});f(e,(0,o.Rm)(n,v,c))};else if(0==(0,o.Gv)(n))a[v]=(e,i)=>{t.update({root:i});const u=(0,r.px)(i,n,null,t);(0,o.KY)(e,v,u)};else{const e=Object.keys(n);(0,o.vA)(1===e.length&&(0,o.Zo)(e[0]),"Not a valid operator");const i=e[0],u=n[i],s=t.context.getOperator(r.i5.PROJECTION,i);!s||"$slice"===i&&!(0,o.eC)(u).every(o.Et)?a[v]=(e,n)=>{t.update({root:n});const s=(0,r.px)(n,u,i,t);(0,o.KY)(e,v,s)}:a[v]=(e,n)=>{t.update({root:n});const r=s(n,u,v,t);(0,o.KY)(e,v,r)}}}const h=1===u.length&&u.includes(i),d=!u.includes(i),p=!s.length,y=p&&h||p&&u.length&&!h;return e=>{const t={};y&&Object.assign(t,e);for(const n in a)a[n](t,e);return p||(0,o.B2)(t),d&&!(0,o.zy)(t,i)&&(0,o.zy)(e,i)&&(t[i]=(0,o.hd)(e,i)),t}}(t,r.qk.init(n),u);return e.map(s)};const s=(e,t,n,r)=>{let i=(0,o.hd)(e,t);(0,o.cy)(i)||(i=(0,o.hd)(i,n)),(0,o.vA)((0,o.cy)(i),"must resolve to array");const u=[];return i.forEach((e,t)=>r({[n]:[e]})&&u.push(t)),u},a=e=>t=>!e(t),c={$and:1,$or:1,$nor:1};function l(e,t,n){const i=Object.entries(t).slice(),u={$and:[],$or:[]};for(let s=0;si.push([e,n,t]))}}const l=e.lastIndexOf("."),f=e.substring(0,l)||e,h=e.substring(l+1);return(t,n)=>{const r=[];for(const[e,o,c]of u.$and)r.push(s(n,e,c,o));if(u.$or.length){const e=[];for(const[t,r,o]of u.$or)e.push(...s(n,t,o,r));r.push((0,o.Am)(e))}const i=(0,o.E$)(r).sort()[0];let a=(0,o.hd)(n,e)[i];f==h||(0,o.Gv)(a)||(a={[h]:a}),(0,o.KY)(t,f,[a])}}function f(e,t){if(e===o.lV||(0,o.gD)(e))return t;if((0,o.gD)(t))return e;for(const n of Object.keys(t))e[n]=f(e[n],t[n]);return e}},703(e,t,n){"use strict";n.d(t,{X2:()=>I});Promise.resolve(!1),Promise.resolve(!0);var r=Promise.resolve();function o(e,t){return e||(e=0),new Promise(function(n){return setTimeout(function(){return n(t)},e)})}function i(){return Math.random().toString(36).substring(2)}var u=0;function s(){var e=1e3*Date.now();return e<=u&&(e=u+1),u=e,e}var a={create:function(e){var t={time:s(),messagesCallback:null,bc:new BroadcastChannel(e),subFns:[]};return t.bc.onmessage=function(e){t.messagesCallback&&t.messagesCallback(e.data)},t},close:function(e){e.bc.close(),e.subFns=[]},onMessage:function(e,t){e.messagesCallback=t},postMessage:function(e,t){try{return e.bc.postMessage(t,!1),r}catch(n){return Promise.reject(n)}},canBeUsed:function(){if("undefined"!=typeof globalThis&&globalThis.Deno&&globalThis.Deno.args)return!0;if("undefined"==typeof window&&"undefined"==typeof self||"function"!=typeof BroadcastChannel)return!1;if(BroadcastChannel._pubkey)throw new Error("BroadcastChannel: Do not overwrite window.BroadcastChannel with this module, this is not a polyfill");return!0},type:"native",averageResponseTime:function(){return 150},microSeconds:s},c=n(5525);function l(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=JSON.parse(JSON.stringify(e));return void 0===t.webWorkerSupport&&(t.webWorkerSupport=!0),t.idb||(t.idb={}),t.idb.ttl||(t.idb.ttl=45e3),t.idb.fallbackInterval||(t.idb.fallbackInterval=150),e.idb&&"function"==typeof e.idb.onclose&&(t.idb.onclose=e.idb.onclose),t.localstorage||(t.localstorage={}),t.localstorage.removeTimeout||(t.localstorage.removeTimeout=6e4),e.methods&&(t.methods=e.methods),t.node||(t.node={}),t.node.ttl||(t.node.ttl=12e4),t.node.maxParallelWrites||(t.node.maxParallelWrites=2048),void 0===t.node.useFastPath&&(t.node.useFastPath=!0),t}var f="messages",h={durability:"relaxed"};function d(){if("undefined"!=typeof indexedDB)return indexedDB;if("undefined"!=typeof window){if(void 0!==window.mozIndexedDB)return window.mozIndexedDB;if(void 0!==window.webkitIndexedDB)return window.webkitIndexedDB;if(void 0!==window.msIndexedDB)return window.msIndexedDB}return!1}function p(e){e.commit&&e.commit()}function y(e,t){var n=e.transaction(f,"readonly",h),r=n.objectStore(f),o=[],i=IDBKeyRange.bound(t+1,1/0);if(r.getAll){var u=r.getAll(i);return new Promise(function(e,t){u.onerror=function(e){return t(e)},u.onsuccess=function(t){e(t.target.result)}})}return new Promise(function(e,u){var s=function(){try{return i=IDBKeyRange.bound(t+1,1/0),r.openCursor(i)}catch(e){return r.openCursor()}}();s.onerror=function(e){return u(e)},s.onsuccess=function(r){var i=r.target.result;i?i.value.ide.lastCursorId&&(e.lastCursorId=t.id),t}).filter(function(t){return function(e,t){return!(e.uuid===t.uuid||t.eMIs.has(e.id)||e.data.time0||e._addEL.internal.length>0}function M(e,t,n){e._addEL[t].push(n),function(e){if(!e._iL&&K(e)){var t=function(t){e._addEL[t.type].forEach(function(e){t.time>=e.time&&e.fn(t.data)})},n=e.method.microSeconds();e._prepP?e._prepP.then(function(){e._iL=!0,e.method.onMessage(e._state,t,n)}):(e._iL=!0,e.method.onMessage(e._state,t,n))}}(e)}function D(e,t,n){e._addEL[t]=e._addEL[t].filter(function(e){return e!==n}),function(e){if(e._iL&&!K(e)){e._iL=!1;var t=e.method.microSeconds();e.method.onMessage(e._state,null,t)}}(e)}I._pubkey=!0,I.prototype={postMessage:function(e){if(this.closed)throw new Error("BroadcastChannel.postMessage(): Cannot post message after channel has closed "+JSON.stringify(e));return T(this,"message",e)},postInternal:function(e){return T(this,"internal",e)},set onmessage(e){var t={time:this.method.microSeconds(),fn:e};D(this,"message",this._onML),e&&"function"==typeof e?(this._onML=t,M(this,"message",t)):this._onML=null},addEventListener:function(e,t){M(this,e,{time:this.method.microSeconds(),fn:t})},removeEventListener:function(e,t){D(this,e,this._addEL[e].find(function(e){return e.fn===t}))},close:function(){var e=this;if(!this.closed){A.delete(this),this.closed=!0;var t=this._prepP?this._prepP:r;return this._onML=null,this._addEL.message=[],t.then(function(){return Promise.all(Array.from(e._uMP))}).then(function(){return Promise.all(e._befC.map(function(e){return e()}))}).then(function(){return e.method.close(e._state)})}},get type(){return this.method.type},get isClosed(){return this.closed}}},1401(e,t,n){"use strict";n.d(t,{I:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.Ig)},1449(e,t,n){"use strict";n.d(t,{A:()=>o});var r=n(1576);function o(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,(0,r.A)(e,t)}},1576(e,t,n){"use strict";function r(e,t){return r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},r(e,t)}n.d(t,{A:()=>r})},1621(e,t,n){"use strict";n.d(t,{X:()=>f});var r=n(4953),o=n(2235);function i(e){return e instanceof u?e:new u(e)}class u{#e=[];#t=[];#n;#r=!1;constructor(e){const t=!(n=e)||"object"!=typeof n&&"function"!=typeof n||"function"!=typeof n[Symbol.iterator]?function(e){return!!e&&"object"==typeof e&&"function"==typeof e?.next}(e)?e:"function"==typeof e?{next:e}:null:e[Symbol.iterator]();var n;(0,o.vA)(!!t,"Iterator must be initialized with an iterable or function.");let r=-1,i={done:!1};this.#n=()=>{for(;!i.done&&(i=t.next(),!i.done);){let e=i.value;r++;if(this.#e.every(({op:t,fn:n})=>{const o=n(e,r);return"map"===t?!!(e=o)||!0:o}))return{value:e,done:!1}}return{done:!0}}}push(e,t){return this.#e.push({op:e,fn:t}),this}next(){return this.#n()}map(e){return this.push("map",e)}filter(e){return this.push("filter",e)}take(e){return(0,o.vA)(e>=0,"value must be a non\u2011negative integer"),this.filter(t=>e-- >0)}drop(e){return(0,o.vA)(e>=0,"value must be a non\u2011negative integer"),this.filter(t=>e--<=0)}transform(e){const t=this;let n;return i(()=>(n||(n=i(e(t.collect()))),n.next()))}collect(){for(;!this.#r;){const{done:e,value:t}=this.#n();e||this.#t.push(t),this.#r=e}return this.#t}each(e){for(let t=this.next();!0!==t.done;t=this.next())e(t.value)}reduce(e,t){let n=this.next();for(void 0!==t||n.done||(t=n.value,n=this.next());!n.done;)t=e(t,n.value),n=this.next();return t}size(){return this.collect().length}[Symbol.iterator](){return this}}var s=n(695);const a={$sort:n(4192).x,$skip:(e,t,n)=>(assert(t>=0,"$skip value must be a non-negative integer"),e.drop(t)),$limit:(e,t,n)=>e.take(t)};class c{#o;#i;#u;#s;#a={};#c=null;#t=[];constructor(e,t,n,r){this.#o=e,this.#i=t,this.#u=n,this.#s=r}fetch(){if(this.#c)return this.#c;this.#c=i(this.#o).filter(this.#i);const e=this.#s.processingMode;e&r.to.CLONE_INPUT&&this.#c.map(e=>(0,o.mg)(e));for(const t of["$sort","$skip","$limit"])(0,o.zy)(this.#a,t)&&(this.#c=a[t](this.#c,this.#a[t],this.#s));return Object.keys(this.#u).length&&(this.#c=(0,s.C)(this.#c,this.#u,this.#s)),e&r.to.CLONE_OUTPUT&&this.#c.map(e=>(0,o.mg)(e)),this.#c}fetchAll(){const e=i(Array.from(this.#t));return this.#t.length=0,function(...e){let t=0;return i(()=>{for(;t0)return this.#t.pop();const e=this.fetch().next();return e.done?void 0:e.value}hasNext(){if(this.#t.length>0)return!0;const e=this.fetch().next();return!e.done&&(this.#t.push(e.value),!0)}[Symbol.iterator](){return this.fetchAll()}}const l=/^\$(and|or|nor|expr|jsonSchema)$/;class f{#l;#f;#s;constructor(e,t){this.#f=(0,o.mg)(e),this.#s=r.qk.init(t).update({condition:e}),this.#l=[],this.compile()}compile(){(0,o.vA)((0,o.Gv)(this.#f),`query criteria must be an object: ${JSON.stringify(this.#f)}`);const e={},t=Object.entries(this.#f);for(const[n,r]of t){if("$where"===n)(0,o.vA)(this.#s.scriptEnabled,"$where operator requires 'scriptEnabled' option to be true."),Object.assign(e,{field:n,expr:r});else if(l.test(n))this.processOperator(n,n,r);else{(0,o.vA)(!(0,o.Zo)(n),`unknown top level operator: ${n}`);for(const[e,t]of Object.entries((0,o.S8)(r)))this.processOperator(n,e,t)}e.field&&this.processOperator(e.field,e.field,e.expr)}}processOperator(e,t,n){const i=this.#s.context.getOperator(r.i5.QUERY,t);(0,o.vA)(!!i,`unknown query operator ${t}`),this.#l.push(i(e,n,this.#s))}test(e){return this.#l.every(t=>t(e))}find(e,t){return new c(e,e=>this.test(e),t||{},this.#s)}}},2080(e,t,n){"use strict";n.d(t,{X:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.XV)},2198(e,t,n){"use strict";n.d(t,{t:()=>o});var r=n(4629),o=function(e){function t(t){var n=e.call(this)||this;return n._value=t,n}return(0,r.C6)(t,e),Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(t){var n=e.prototype._subscribe.call(this,t);return!n.closed&&t.next(this._value),n},t.prototype.getValue=function(){var e=this,t=e.hasError,n=e.thrownError,r=e._value;if(t)throw n;return this._throwIfClosed(),r},t.prototype.next=function(t){e.prototype.next.call(this,this._value=t)},t}(n(9092).B)},2226(e,t,n){"use strict";n.d(t,{f:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.fy)},2235(e,t,n){"use strict";n.d(t,{lV:()=>d,yk:()=>te,vA:()=>O,mg:()=>$,UD:()=>w,eC:()=>N,B2:()=>Q,Bq:()=>L,$z:()=>z,zy:()=>q,E$:()=>F,cy:()=>A,Lm:()=>P,$P:()=>T,Im:()=>R,n4:()=>x,gD:()=>M,Et:()=>S,Gv:()=>j,Zo:()=>J,gd:()=>K,Kg:()=>C,S8:()=>ee,yT:()=>Z,hd:()=>W,Rm:()=>Y,KY:()=>H,vN:()=>D,QP:()=>E,Am:()=>U});const r=[];function o(e,t){return 16777619*e^t>>>0}function i(e){if(Number.isNaN(e))return 2143289344;if(!Number.isFinite(e))return e>0?2139095040:4286578688;const t=Math.trunc(e),n=e-t;let r=0|t;if(0!==n){r=o(r,0|Math.floor(4294967296*n))}return r>>>0}function u(e){let t=0;for(let n=0;n>>0}function s(e){let t=u(e.constructor.name);return t=o(t,function(e){let t=0;for(let n=0;n>>0}(new Uint8Array(e.buffer,e.byteOffset,e.byteLength))),t>>>0}const a=[3735928559,305441741].map(e=>o(3,e)),c=o(1,0),l=o(2,0);function f(e,t){if(null===e)return c;switch(typeof e){case"undefined":return l;case"boolean":return a[+e];case"number":return o(4,i(e));case"string":return o(5,u(e));case"bigint":return o(6,function(e){let t=0;const n=e<0n;let r=n?-e:e;if(0n===r)t=o(t,0);else for(;r>0n;)t=o(t,Number(0xffn&r)),r>>=8n;return o(t,+n)>>>0}(e));case"function":return o(7,function(e){let t=u((e.name||"")+e.toString());return t=o(t,e.length),t>>>0}(e));default:if(ArrayBuffer.isView(e)&&!(e instanceof DataView))return o(12,s(e));if(e instanceof Date)return o(10,i(e.getTime()));if(e instanceof RegExp){return o(11,o(u(e.source),u(e.flags)))}return Array.isArray(e)?o(8,function(e,t){if(t.has(e))return 13;t.add(e);let n=1;for(let r=0;r>>0}(e,t)):o(9,function(e,t){if(t.has(e))return 13;if(t.add(e),r.length=0,Object.getPrototypeOf(e)===Object.prototype)for(const o in e)r.push(o);else{Array.prototype.push.apply(r,Object.keys(e));for(const t of Object.getOwnPropertyNames(Object.getPrototypeOf(e)))"function"!=typeof e[t]&&r.push(t)}r.sort();let n=u(e?.constructor?.name);for(const i of r)n=o(n,u(i)),n=o(n,f(e[i],t));return t.delete(e),n>>>0}(e,t))}}class h extends Error{}const d=Symbol("missing"),p=e=>"object"!=typeof e&&"function"!=typeof e||null===e,y=e=>p(e)||T(e)||K(e),v={undefined:1,null:2,number:3,string:4,symbol:5,object:6,array:7,arraybuffer:8,boolean:9,date:10,regexp:11,function:12},m=2*Object.keys(v).length,g=(e,t)=>et?1:0;function b(e,t,n=!1){let r=0;if(e===d&&(e=void 0),t===d&&(t=void 0),e===t||Object.is(e,t))return 0;const o=B(e)?"arraybuffer":E(e),i=B(t)?"arraybuffer":E(t);if(o!==i){if("undefined"==o)return-1;if("undefined"==i)return 1;if(n&&"array"==o&&!e.length)return-1;if(n&&"array"==i&&!t.length)return 1;if(n){if(A(e)){const n=e.slice().sort(b);r=1;for(const e of n)if((r=Math.min(r,b(e,t)))<0)return r;return r}if(A(t)){const n=t.slice().sort(b);r=-1;for(const t of n)if((r=Math.max(r,b(e,t)))>0)return r;return r}}const u=v[o]??m,s=v[i]??m;if(u!==s)return g(u,s)}switch(o){case"number":case"string":return g(e,t);case"boolean":case"date":return g(+e,+t);case"regexp":return(r=g(e.source,t.source))||(r=g(e.flags,t.flags))?r:0;case"arraybuffer":return((e,t)=>{const n=new Uint8Array(e.buffer,e.byteOffset,e.byteLength),r=new Uint8Array(t.buffer,t.byteOffset,t.byteLength),o=Math.min(n.length,r.length);for(let i=0;inull!=e&&e.toString!==Object.prototype.toString;function x(e,t){if(e===t||Object.is(e,t))return!0;if(null===e||null===t)return!1;if(typeof e!=typeof t)return!1;if("object"!=typeof e)return!1;if(e.constructor!==t.constructor)return!1;if(T(e))return T(t)&&+e===+t;if(K(e))return K(t)&&e.source===t.source&&e.flags===t.flags;if(A(e)&&A(t))return e.length===t.length&&e.every((e,n)=>x(e,t[n]));if(e?.constructor!==Object&&_(e))return e?.toString()===t?.toString();const n=Object.keys(e),r=Object.keys(t);return n.length===r.length&&n.every(n=>q(t,n)&&x(e[n],t[n]))}class k extends Map{#h=new Map;#d=e=>{const t=f(e,new WeakSet)>>>0;return[(this.#h.get(t)||[]).find(t=>x(t,e)),t]};constructor(){super()}static init(){return new k}clear(){super.clear(),this.#h.clear()}delete(e){if(p(e))return super.delete(e);const[t,n]=this.#d(e);return!!super.delete(t)&&(this.#h.set(n,this.#h.get(n).filter(e=>!x(e,t))),!0)}get(e){if(p(e))return super.get(e);const[t,n]=this.#d(e);return super.get(t)}has(e){if(p(e))return super.has(e);const[t,n]=this.#d(e);return super.has(t)}set(e,t){if(p(e))return super.set(e,t);const[n,r]=this.#d(e);if(super.has(n))super.set(n,t);else{super.set(e,t);const n=this.#h.get(r)||[];n.push(e),this.#h.set(r,n)}return this}get size(){return super.size}}function O(e,t){if(!e)throw new h(t)}function E(e){if(null===e)return"null";const t=typeof e;return"object"!==t&&v[t]?t:A(e)?"array":T(e)?"date":K(e)?"regexp":e?.constructor?.name?.toLowerCase()??"object"}const P=e=>"boolean"==typeof e,C=e=>"string"==typeof e,S=e=>!isNaN(e)&&"number"==typeof e,A=Array.isArray,j=e=>"object"===E(e),I=e=>!p(e),T=e=>e instanceof Date,K=e=>e instanceof RegExp,M=e=>null==e,D=(e,t=!0)=>!!e||t&&""===e,R=e=>M(e)||C(e)&&!e||A(e)&&0===e.length||j(e)&&0===Object.keys(e).length,N=e=>A(e)?e:[e],q=(e,t)=>!!e&&Object.prototype.hasOwnProperty.call(e,t),B=e=>"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView(e),$=(e,t)=>{if(M(e)||P(e)||S(e)||C(e))return e;if(T(e))return new Date(e);if(K(e))return new RegExp(e);if(B(e)){return new(0,e.constructor)(e)}if(t instanceof Set||(t=new Set),t.has(e))throw new Error("mingo: cycle detected while processing object/array");t.add(e);try{if(A(e)){const n=new Array(e.length);for(let r=0;r0===e.length))return[];if(1===e.length)return e[0].slice();const t=[k.init(),k.init()];e[e.length-1].forEach(e=>t[0].set(e,!0));for(let n=e.length-2;n>-1;n--){if(e[n].forEach(e=>{t[0].has(e)&&t[1].set(e,!0)}),0===t[1].size)return[];t.reverse(),t[1].clear()}return Array.from(t[0].keys())}function L(e,t=1){const n=new Array;return function e(t,r){for(let o=0,i=t.length;o0||r<0)?e(t[o],Math.max(-1,r-1)):n.push(t[o])}(e,t),n}function U(e){const t=k.init();return e.forEach(e=>t.set(e,!0)),Array.from(t.keys())}function z(e,t){if(e.length<1)return new Map;const n=k.init();for(let r=0;r0)break;r+=1;const t=n.slice(i);o=o.reduce((n,r)=>{const o=e(r,t);return void 0!==o&&n.push(o),n},[]);break}if(o=V(o,t),void 0===o)break}return o}(e,t.split("."));return A(o)&&n?.unwrapArray?function(e,t){if(t<1)return e;for(;t--&&1===e.length&&A(e[0]);)e=e[0];return e}(o,r):o}function Y(e,t,n){const r=t.indexOf("."),o=-1==r?t:t.substring(0,r),i=t.substring(r+1),u=-1!=r;if(A(e)){const r=/^\d+$/.test(o),s=r&&n?.preserveIndex?e.slice():[];if(r){const t=parseInt(o);let r=V(e,t);u&&(r=Y(r,i,n)),n?.preserveIndex?s[t]=r:s.push(r)}else for(const o of e){const e=Y(o,t,n);n?.preserveMissing?s.push(null==e?d:e):(null!=e||n?.preserveIndex)&&s.push(e)}return s}const s=n?.preserveKeys?{...e}:{};let a=V(e,o);if(u&&(a=Y(a,i,n)),void 0!==a)return s[o]=a,s}function Q(e){if(A(e))for(let t=e.length-1;t>=0;t--)e[t]===d?e.splice(t,1):Q(e[t]);else if(j(e))for(const t of Object.keys(e))q(e,t)&&Q(e[t])}const G=/^\d+$/;function X(e,t,n,r){const o=t.split("."),i=o[0],u=o.slice(1).join(".");if(1===o.length)(j(e)||A(e)&&G.test(i))&&n(e,i);else{r?.buildGraph&&M(e[i])&&(e[i]={});const t=e[i];if(!t)return;const s=!!(o.length>1&&G.test(o[1]));A(t)&&r?.descendArray&&!s?t.forEach(e=>X(e,u,n,r)):X(t,u,n,r)}}function H(e,t,n){X(e,t,(e,t)=>e[t]=n,{buildGraph:!0})}function Z(e,t,n){X(e,t,(e,t)=>{A(e)?e.splice(parseInt(t),1):j(e)&&delete e[t]},n)}const J=e=>e&&"$"===e[0]&&/^\$[a-zA-Z0-9_]+$/.test(e);function ee(e){if(y(e))return K(e)?{$regex:e}:{$eq:e};if(I(e)){if(!Object.keys(e).some(J))return{$eq:e};if(q(e,"$regex")){const t={...e};return t.$regex=new RegExp(e.$regex,e.$options),delete t.$options,t}}return e}class te{constructor(){this.root={children:new Map,isTerminal:!1}}add(e){const t=e.split(".");let n=this.root;for(const r of t){if(n.isTerminal)return!1;n.children.has(r)||n.children.set(r,{children:new Map,isTerminal:!1}),n=n.children.get(r)}return!n.isTerminal&&!n.children.size&&(n.isTerminal=!0)}}},2442(e,t,n){"use strict";n.d(t,{Z:()=>c});var r=n(7708),o=n(7688),i=n(5096),u=n(8071),s=n(4370);var a=n(1693);function c(e,t,n){return void 0===n&&(n=1/0),(0,a.T)(t)?c(function(n,i){return(0,r.T)(function(e,r){return t(n,e,i,r)})((0,o.Tg)(e(n,i)))},n):("number"==typeof t&&(n=t),(0,i.N)(function(t,r){return function(e,t,n,r,i,a,c,l){var f=[],h=0,d=0,p=!1,y=function(){!p||f.length||h||t.complete()},v=function(e){return ho});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.Q_)},2830(e,t,n){"use strict";n.d(t,{J:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.Jy)},3026(e,t,n){"use strict";n.d(t,{C:()=>i,U:()=>u});var r=n(4629),o=n(1693);function i(e){return(0,r.AQ)(this,arguments,function(){var t,n,o;return(0,r.YH)(this,function(i){switch(i.label){case 0:t=e.getReader(),i.label=1;case 1:i.trys.push([1,,9,10]),i.label=2;case 2:return[4,(0,r.N3)(t.read())];case 3:return n=i.sent(),o=n.value,n.done?[4,(0,r.N3)(void 0)]:[3,5];case 4:return[2,i.sent()];case 5:return[4,(0,r.N3)(o)];case 6:return[4,i.sent()];case 7:return i.sent(),[3,2];case 8:return[3,10];case 9:return t.releaseLock(),[7];case 10:return[2]}})})}function u(e){return(0,o.T)(null==e?void 0:e.getReader)}},3356(e,t,n){"use strict";n.d(t,{Z:()=>a});var r=n(5499);var o=n(235),i=n(9852);function u(){for(var e=[],t=0;ti});var r=n(1621),o=n(2235);const i=(e,t,n)=>{(0,o.vA)((0,o.cy)(t),"Invalid expression. $or expects value to be an Array");const i=t.map(e=>new r.X(e,n));return e=>i.some(t=>t.test(e))}},3697(e,t,n){"use strict";n.d(t,{o:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.oZ)},3819(e,t,n){"use strict";n.d(t,{T:()=>o});var r=n(1693);function o(e){return Symbol.asyncIterator&&(0,r.T)(null==e?void 0:e[Symbol.asyncIterator])}},3836(e,t,n){"use strict";function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}function o(e){var t=function(e,t){if("object"!=r(e)||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t||"default");if("object"!=r(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==r(t)?t:t+""}function i(e,t){for(var n=0;nu})},4157(e,t,n){"use strict";n.d(t,{h:()=>a});var r=n(5499),o=n(7688),i=new(n(1753).c)(function(e){return e.complete()});var u=n(235),s=n(9852);function a(){for(var e=[],t=0;to});var r=n(2235);const o=(e,t,n)=>{if((0,r.Im)(t)||!(0,r.Gv)(t))return e;let o=r.UD;const u=n.collation;return(0,r.Gv)(u)&&(0,r.Kg)(u.locale)&&(o=function(e){const t={sensitivity:i[e.strength||3],caseFirst:"off"===e.caseFirst?"false":e.caseFirst||"false",numeric:e.numericOrdering||!1,ignorePunctuation:"shifted"===e.alternate};!0===e.caseLevel&&("base"===t.sensitivity&&(t.sensitivity="case"),"accent"===t.sensitivity&&(t.sensitivity="variant"));const n=new Intl.Collator(e.locale,t);return(e,t)=>{if(!(0,r.Kg)(e)||!(0,r.Kg)(t))return(0,r.UD)(e,t);const o=n.compare(e,t);return o<0?-1:o>0?1:0}}(u)),e.transform(e=>{const n=Object.keys(t);for(const i of n.reverse()){const n=(0,r.$z)(e,e=>(0,r.hd)(e,i)),u=Array.from(n.keys());let s=!1;if(o===r.UD){let e=!0,t=!0;s=u.every(n=>+(e&&=(0,r.Kg)(n))^+(t&&=(0,r.Et)(n))),e?u.sort():t&&new Float64Array(u).sort().forEach((e,t)=>u[t]=e)}s||u.sort(o),-1===t[i]&&u.reverse();let a=0;for(const t of u)for(const r of n.get(t))e[a++]=r;(0,r.vA)(a==e.length,"bug: counter must match collection size.")}return e})},i={1:"base",2:"accent",3:"variant"}},4207(e,t,n){"use strict";n.d(t,{l:()=>r});var r="function"==typeof Symbol&&Symbol.iterator?Symbol.iterator:"@@iterator"},4429(e,t,n){"use strict";function r(e){return new TypeError("You provided "+(null!==e&&"object"==typeof e?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}n.d(t,{L:()=>r})},4612(e,t,n){"use strict";n.d(t,{P:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.Pp)},4799(e,t,n){"use strict";n.d(t,{x:()=>i});var r=n(4207),o=n(1693);function i(e){return(0,o.T)(null==e?void 0:e[r.l])}},4953(e,t,n){"use strict";n.d(t,{i5:()=>u,ob:()=>s,px:()=>a,qk:()=>i,to:()=>o});var r=n(2235),o=(e=>(e[e.CLONE_OFF=0]="CLONE_OFF",e[e.CLONE_INPUT=1]="CLONE_INPUT",e[e.CLONE_OUTPUT=2]="CLONE_OUTPUT",e[e.CLONE_ALL=3]="CLONE_ALL",e))(o||{});class i{constructor(e,t){this.options=e,this.#p=t?{...t}:{}}#p;static init(e){return e instanceof i?new i(e.options,e.#p):new i({idKey:"_id",scriptEnabled:!0,useStrictMode:!0,processingMode:0,...e,context:e?.context?s.from(e?.context):s.init()})}update(e){return Object.assign(this.#p,e,{timestamp:this.#p.timestamp,variables:{...this.#p?.variables,...e?.variables}}),this}get local(){return this.#p}get now(){return this.#p?.timestamp||Object.assign(this.#p,{timestamp:Date.now()}),new Date(this.#p.timestamp)}get idKey(){return this.options.idKey}get collation(){return this.options?.collation}get processingMode(){return this.options?.processingMode}get useStrictMode(){return this.options?.useStrictMode}get scriptEnabled(){return this.options?.scriptEnabled}get collectionResolver(){return this.options?.collectionResolver}get jsonSchemaValidator(){return this.options?.jsonSchemaValidator}get variables(){return this.options?.variables}get context(){return this.options?.context}}var u=(e=>(e.ACCUMULATOR="accumulator",e.EXPRESSION="expression",e.PIPELINE="pipeline",e.PROJECTION="projection",e.QUERY="query",e.WINDOW="window",e))(u||{});class s{#a=new Map(Object.values(u).map(e=>[e,{}]));constructor(){}static init(e={}){const t=new s;for(const[n,r]of Object.entries(e))t.#a.has(n)&&r&&t.addOps(n,r);return t}static from(...e){const t=new s;for(const n of e)for(const e of Object.values(u))t.addOps(e,n.#a.get(e));return t}addOps(e,t){return this.#a.set(e,Object.assign({},t,this.#a.get(e))),this}getOperator(e,t){return this.#a.get(e)[t]??null}addAccumulatorOps(e){return this.addOps("accumulator",e)}addExpressionOps(e){return this.addOps("expression",e)}addQueryOps(e){return this.addOps("query",e)}addPipelineOps(e){return this.addOps("pipeline",e)}addProjectionOps(e){return this.addOps("projection",e)}addWindowOps(e){return this.addOps("window",e)}}function a(e,t,n,o){const u=o instanceof i&&!(0,r.gD)(o.local.root)?o:i.init(o).update({root:e});return(0,r.Zo)(n)?f(e,t,n,u):l(e,t,u)}const c=["$$ROOT","$$CURRENT","$$REMOVE","$$NOW"];function l(e,t,n){if((0,r.Kg)(t)&&t.length>0&&"$"===t[0]){if("$$KEEP"===t||"$$PRUNE"===t||"$$DESCEND"===t)return t;let o=n.local.root;const i=t.split(".");if(c.includes(i[0])){switch(i[0]){case"$$ROOT":break;case"$$CURRENT":o=e;break;case"$$REMOVE":o=void 0;break;case"$$NOW":o=new Date(n.now)}t=t.slice(i[0].length+1)}else if("$$"===i[0].slice(0,2)){o=Object.assign({},n.variables,{this:e},n?.local?.variables);const u=i[0].slice(2);(0,r.vA)((0,r.zy)(o,u),`Use of undefined variable: ${u}`),t=t.slice(2)}else t=t.slice(1);return""===t?o:(0,r.hd)(o,t)}if((0,r.cy)(t))return t.map(t=>l(e,t,n));if((0,r.Gv)(t)){const o={},i=Object.entries(t);for(const[u,s]of i){if((0,r.Zo)(u))return(0,r.vA)(1===i.length,`Expression must contain a single operator. got [${Object.keys(t).join(",")}]`),f(e,s,u,n);o[u]=l(e,s,n)}return o}return t}function f(e,t,n,o){const i=o.context,u=i.getOperator("expression",n);if(u)return u(e,t,o);const s=i.getOperator("accumulator",n);return(0,r.vA)(!!s,`accumulator '${n}' is not registered.`),(0,r.cy)(e)||(e=l(e,t,o),t=null),(0,r.vA)((0,r.cy)(e),`arguments must resolve to array for ${n}.`),s(e,t,o)}},5499(e,t,n){"use strict";n.d(t,{U:()=>i});var r=n(2442),o=n(3887);function i(e){return void 0===e&&(e=1/0),(0,r.Z)(o.D,e)}},5525(e,t,n){"use strict";n.d(t,{p4:()=>r});class r{ttl;map=new Map;_to=!1;constructor(e){this.ttl=e}has(e){const t=this.map.get(e);return void 0!==t&&(!(t{this._to=!1,function(e){const t=o()-e.ttl,n=e.map[Symbol.iterator]();for(;;){const r=n.next().value;if(!r)break;const o=r[0];if(!(r[1]o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.GU)},5714(e,t,n){"use strict";n.d(t,{X:()=>c});var r=n(4629),o=n(5096),i=n(5499),u=n(235),s=n(9852);function a(){for(var e=[],t=0;to});var r=n(1693);function o(e){return(0,r.T)(null==e?void 0:e.then)}},5805(e,t,n){"use strict";n.d(t,{E:()=>i});var r=n(1621),o=n(2235);const i=(e,t,n)=>{const i={};i[e]=(0,o.S8)(t);const u=new r.X(i,n);return e=>!u.test(e)}},5912(e,t,n){"use strict";n.d(t,{W:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.WP)},5964(e,t,n){"use strict";n.d(t,{C5:()=>a,GU:()=>l,Ig:()=>m,Jy:()=>b,MR:()=>d,NV:()=>f,ON:()=>u,Pp:()=>y,Q_:()=>h,TU:()=>k,WP:()=>v,XV:()=>s,fy:()=>p,oZ:()=>c});var r=n(4953),o=n(1621),i=n(2235);function u(e,t,n,o){const u={unwrapArray:!0},s=Math.max(1,e.split(".").length-1),a=r.qk.init(n).update({depth:s});return n=>{const r=(0,i.hd)(n,e,u);return o(r,t,a)}}function s(e,t,n){if((0,i.n4)(e,t))return!0;if((0,i.gD)(e)&&(0,i.gD)(t))return!0;if((0,i.cy)(e)){const r=n?.local?.depth??1;return e.some(e=>(0,i.n4)(e,t))||(0,i.Bq)(e,r).some(e=>(0,i.n4)(e,t))}return!1}function a(e,t,n){return!s(e,t,n)}function c(e,t,n){return(0,i.gD)(e)?t.some(e=>null===e):(0,i.E$)([(0,i.eC)(e),t]).length>0}function l(e,t,n){return!c(e,t)}function f(e,t,n){return O(e,t,(e,t)=>(0,i.UD)(e,t)<0)}function h(e,t,n){return O(e,t,(e,t)=>(0,i.UD)(e,t)<=0)}function d(e,t,n){return O(e,t,(e,t)=>(0,i.UD)(e,t)>0)}function p(e,t,n){return O(e,t,(e,t)=>(0,i.UD)(e,t)>=0)}function y(e,t,n){return(0,i.eC)(e).some(e=>2===t.length&&e%t[0]===t[1])}function v(e,t,n){const r=(0,i.eC)(e),o=e=>(0,i.Kg)(e)&&(0,i.vN)(t.exec(e),n?.useStrictMode);return r.some(o)||(0,i.Bq)(r,1).some(o)}function m(e,t,n){return Array.isArray(e)&&e.length===t}function g(e){return(0,i.Zo)(e)&&-1===["$and","$or","$nor"].indexOf(e)}function b(e,t,n){if((0,i.cy)(e)&&!(0,i.Im)(e)){let r=e=>e,i=t;Object.keys(t).every(g)&&(i={temp:t},r=e=>({temp:e}));const u=new o.X(i,n);for(let t=0,n=e.length;tnull===e,_={array:i.cy,boolean:i.Lm,bool:i.Lm,date:i.$P,number:i.Et,int:i.Et,long:i.Et,double:i.Et,decimal:i.Et,null:w,object:i.Gv,regexp:i.gd,regex:i.gd,string:i.Kg,undefined:i.gD,1:i.Et,2:i.Kg,3:i.Gv,4:i.cy,6:i.gD,8:i.Lm,9:i.$P,10:w,11:i.gd,16:i.Et,18:i.Et,19:i.Et};function x(e,t,n){const r=_[t];return!!r&&r(e)}function k(e,t,n){return(0,i.cy)(t)?t.findIndex(t=>x(e,t))>=0:x(e,t)}function O(e,t,n){return(0,i.eC)(e).some(e=>(0,i.QP)(e)===(0,i.QP)(t)&&n(e,t))}},6114(e,t,n){"use strict";n.d(t,{kC:()=>B,Cs:()=>$});const r=e=>{e.previousResults.unshift(e.changeEvent.doc),e.keyDocumentMap&&e.keyDocumentMap.set(e.changeEvent.id,e.changeEvent.doc)},o=e=>{e.previousResults.push(e.changeEvent.doc),e.keyDocumentMap&&e.keyDocumentMap.set(e.changeEvent.id,e.changeEvent.doc)},i=e=>{const t=e.previousResults.shift();e.keyDocumentMap&&t&&e.keyDocumentMap.delete(t[e.queryParams.primaryKey])},u=e=>{const t=e.previousResults.pop();e.keyDocumentMap&&t&&e.keyDocumentMap.delete(t[e.queryParams.primaryKey])},s=e=>{e.keyDocumentMap&&e.keyDocumentMap.delete(e.changeEvent.id);const t=e.queryParams.primaryKey,n=e.previousResults;for(let r=0;r{const t=e.changeEvent.id,n=e.changeEvent.doc;if(e.keyDocumentMap){if(e.keyDocumentMap.has(t))return;e.keyDocumentMap.set(t,n)}else{if(e.previousResults.find(n=>n[e.queryParams.primaryKey]===t))return}!function(e,t,n,r){var o,i=e.length,u=i-1,s=0;if(0===i)return e.push(t),0;for(;r<=u;)n(o=e[s=r+(u-r>>1)],t)<=0?r=s+1:u=s-1;n(o,t)<=0&&s++,e.splice(s,0,t)}(e.previousResults,n,e.queryParams.sortComparator,0)},c=["doNothing","insertFirst","insertLast","removeFirstItem","removeLastItem","removeFirstInsertLast","removeLastInsertFirst","removeFirstInsertFirst","removeLastInsertLast","removeExisting","replaceExisting","alwaysWrong","insertAtSortPosition","removeExistingAndInsertAtSortPosition","runFullQueryAgain","unknownAction"],l={doNothing:e=>{},insertFirst:r,insertLast:o,removeFirstItem:i,removeLastItem:u,removeFirstInsertLast:e=>{i(e),o(e)},removeLastInsertFirst:e=>{u(e),r(e)},removeFirstInsertFirst:e=>{i(e),r(e)},removeLastInsertLast:e=>{u(e),o(e)},removeExisting:s,replaceExisting:e=>{const t=e.changeEvent.doc,n=e.queryParams.primaryKey,r=e.previousResults;for(let o=0;o{const t={_id:"wrongHuman"+(new Date).getTime()};e.previousResults.length=0,e.previousResults.push(t),e.keyDocumentMap&&(e.keyDocumentMap.clear(),e.keyDocumentMap.set(t._id,t))},insertAtSortPosition:a,removeExistingAndInsertAtSortPosition:e=>{s(e),a(e)},runFullQueryAgain:e=>{throw new Error("Action runFullQueryAgain must be implemented by yourself")},unknownAction:e=>{throw new Error("Action unknownAction should never be called")}};function f(e){return e?"1":"0"}!function(e=6){let t="";const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";for(let r=0;r!!e.queryParams.limit,g=e=>1===e.queryParams.limit,b=e=>!!(e.queryParams.skip&&e.queryParams.skip>0),w=e=>"DELETE"===e.changeEvent.operation,_=e=>"INSERT"===e.changeEvent.operation,x=e=>"UPDATE"===e.changeEvent.operation,k=e=>m(e)&&e.previousResults.length>=e.queryParams.limit,O=e=>{const t=e.queryParams.sortFields,n=e.changeEvent.previous,r=e.changeEvent.doc;if(!r)return!1;if(!n)return!0;for(let o=0;o{const t=e.changeEvent.id;if(e.keyDocumentMap){return e.keyDocumentMap.has(t)}{const n=e.queryParams.primaryKey,r=e.previousResults;for(let e=0;e{const t=e.previousResults[0];return!(!t||t[e.queryParams.primaryKey]!==e.changeEvent.id)},C=e=>{const t=p(e.previousResults);return!(!t||t[e.queryParams.primaryKey]!==e.changeEvent.id)},S=e=>{const t=e.changeEvent.previous;if(!t)return!1;const n=e.previousResults[0];if(!n)return!1;if(n[e.queryParams.primaryKey]===e.changeEvent.id)return!0;return e.queryParams.sortComparator(t,n)<0},A=e=>{const t=e.changeEvent.previous;if(!t)return!1;const n=p(e.previousResults);if(!n)return!1;if(n[e.queryParams.primaryKey]===e.changeEvent.id)return!0;return e.queryParams.sortComparator(t,n)>0},j=e=>{const t=e.changeEvent.doc;if(!t)return!1;const n=e.previousResults[0];if(!n)return!1;if(n[e.queryParams.primaryKey]===e.changeEvent.id)return!0;return e.queryParams.sortComparator(t,n)<0},I=e=>{const t=e.changeEvent.doc;if(!t)return!1;const n=p(e.previousResults);if(!n)return!1;if(n[e.queryParams.primaryKey]===e.changeEvent.id)return!0;return e.queryParams.sortComparator(t,n)>0},T=e=>{const t=e.changeEvent.previous;return!!t&&e.queryParams.queryMatcher(t)},K=e=>{const t=e.changeEvent.doc;if(!t)return!1;return e.queryParams.queryMatcher(t)},M=e=>0===e.previousResults.length,D={0:_,1:x,2:w,3:m,4:g,5:b,6:M,7:k,8:P,9:C,10:O,11:E,12:S,13:A,14:j,15:I,16:T,17:K};let R;function N(){return R||(R=function(e){const t=new Map,n=2+2*parseInt(e.charAt(0)+e.charAt(1),10),r=h(e.substring(2,n),2);for(let a=0;afunction(e,t,n){let r=e,o=e.l;for(;;){if(r=r[f(t[o](n))],"number"==typeof r||"string"==typeof r)return r;o=r.l}}(N(),D,e);function B(e){const t=q(e);return c[t]}function $(e,t,n,r,o){return(0,l[e])({queryParams:t,changeEvent:n,previousResults:r,keyDocumentMap:o}),r}},6343(e,t,n){"use strict";function r(e){return r=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},r(e)}n.d(t,{A:()=>u});var o=n(1576);function i(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(i=function(){return!!e})()}function u(e){var t="function"==typeof Map?new Map:void 0;return u=function(e){if(null===e||!function(e){try{return-1!==Function.toString.call(e).indexOf("[native code]")}catch(t){return"function"==typeof e}}(e))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,n)}function n(){return function(e,t,n){if(i())return Reflect.construct.apply(null,arguments);var r=[null];r.push.apply(r,t);var u=new(e.bind.apply(e,r));return n&&(0,o.A)(u,n.prototype),u}(e,arguments,r(this).constructor)}return n.prototype=Object.create(e.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),(0,o.A)(n,e)},u(e)}},6529(e,t,n){"use strict";n.d(t,{M:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.MR)},6729(e,t,n){"use strict";n.d(t,{a:()=>i});var r=n(1621),o=n(2235);const i=(e,t,n)=>{(0,o.vA)((0,o.cy)(t),"Invalid expression: $and expects value to be an Array.");const i=t.map(e=>new r.X(e,n));return e=>i.every(t=>t.test(e))}},7329(e,t,n){"use strict";n.d(t,{cf:()=>i});var r=n(8900);const o=Symbol.for("Dexie"),i=globalThis[o]||(globalThis[o]=r);if(r.semVer!==i.semVer)throw new Error(`Two different versions of Dexie loaded in the same app: ${r.semVer} and ${i.semVer}`);const{liveQuery:u,mergeRanges:s,rangesOverlap:a,RangeSet:c,cmp:l,Entity:f,PropModSymbol:h,PropModification:d,replacePrefix:p,add:y,remove:v}=i},7531(e,t,n){"use strict";n.d(t,{T:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.TU)},7635(e,t,n){"use strict";n.d(t,{G:()=>r});var r=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;this._parallels=e||1,this._qC=0,this._iC=new Set,this._lHN=0,this._hPM=new Map,this._pHM=new Map};function o(e,t){if(t){if(t._timeoutObj&&clearTimeout(t._timeoutObj),e._pHM.has(t)){var n=e._pHM.get(t);e._hPM.delete(n),e._pHM.delete(t)}e._iC.delete(t)}}function i(e){e._tryIR||0===e._iC.size||(e._tryIR=!0,setTimeout(function(){e.isIdle()?setTimeout(function(){e.isIdle()?(!function(e){0!==e._iC.size&&(e._iC.values().next().value._manRes(),setTimeout(function(){return i(e)},0))}(e),e._tryIR=!1):e._tryIR=!1},0):e._tryIR=!1},0))}r.prototype={isIdle:function(){return this._qCy});var r=n(4629),o=n(9739),i=n(5796),u=n(1753),s=n(9065),a=n(3819),c=n(4429),l=n(4799),f=n(3026),h=n(1693),d=n(6712),p=n(8896);function y(e){if(e instanceof u.c)return e;if(null!=e){if((0,s.l)(e))return g=e,new u.c(function(e){var t=g[p.s]();if((0,h.T)(t.subscribe))return t.subscribe(e);throw new TypeError("Provided object does not correctly implement Symbol.observable")});if((0,o.X)(e))return m=e,new u.c(function(e){for(var t=0;tr})},8146(e,t,n){"use strict";n.d(t,{p:()=>i});var r=n(5096),o=n(4370);function i(e,t){return(0,r.N)(function(n,r){var i=0;n.subscribe((0,o._)(r,function(n){return e.call(t,n,i++)&&r.next(n)}))})}},8259(e,t,n){"use strict";n.d(t,{P:()=>o});var r=n(2235);const o=(e,t,n)=>{const o=e.includes("."),i=!!t;return!o||e.match(/\.\d+$/)?t=>void 0!==(0,r.hd)(t,e)===i:t=>{const n=(0,r.Rm)(t,e,{preserveIndex:!0}),o=(0,r.hd)(n,e.substring(0,e.lastIndexOf(".")));return(0,r.cy)(o)?o.some(e=>void 0!==e)===i:void 0!==o===i}}},8609(e,t,n){"use strict";n.d(t,{t:()=>f});var r=n(4629),o=n(9092),i={now:function(){return(i.delegate||Date).now()},delegate:void 0},u=function(e){function t(t,n,r){void 0===t&&(t=1/0),void 0===n&&(n=1/0),void 0===r&&(r=i);var o=e.call(this)||this;return o._bufferSize=t,o._windowTime=n,o._timestampProvider=r,o._buffer=[],o._infiniteTimeWindow=!0,o._infiniteTimeWindow=n===1/0,o._bufferSize=Math.max(1,t),o._windowTime=Math.max(1,n),o}return(0,r.C6)(t,e),t.prototype.next=function(t){var n=this,r=n.isStopped,o=n._buffer,i=n._infiniteTimeWindow,u=n._timestampProvider,s=n._windowTime;r||(o.push(t),!i&&o.push(u.now()+s)),this._trimBuffer(),e.prototype.next.call(this,t)},t.prototype._subscribe=function(e){this._throwIfClosed(),this._trimBuffer();for(var t=this._innerSubscribe(e),n=this._infiniteTimeWindow,r=this._buffer.slice(),o=0;o0&&(t=new a.Ms({next:function(e){return g.next(e)},error:function(e){p=!0,y(),r=l(v,i,e),g.error(e)},complete:function(){h=!0,y(),r=l(v,f),g.complete()}}),(0,s.Tg)(e).subscribe(t))})(e)}}({connector:function(){return new u(h,t,n)},resetOnError:!0,resetOnComplete:!1,resetOnRefCountZero:d})}},8900(e){e.exports=function(){"use strict";var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])})(t,n)},t=function(){return(t=Object.assign||function(e){for(var t,n=1,r=arguments.length;n.",Je="String expected.",et=[],tt="__dbnames",nt="readonly",rt="readwrite";function ot(e,t){return e?t?function(){return e.apply(this,arguments)&&t.apply(this,arguments)}:e:t}var it={type:3,lower:-1/0,lowerOpen:!1,upper:[[]],upperOpen:!1};function ut(e){return"string"!=typeof e||/\./.test(e)?function(e){return e}:function(t){return void 0===t[e]&&e in t&&delete(t=S(t))[e],t}}function st(){throw V.Type()}function at(e,t){try{var n=ct(e),r=ct(t);if(n!==r)return"Array"===n?1:"Array"===r?-1:"binary"===n?1:"binary"===r?-1:"string"===n?1:"string"===r?-1:"Date"===n?1:"Date"!==r?NaN:-1;switch(n){case"number":case"Date":case"string":return tn+u&&o(n+h)})})}var i=yt(n)&&n.limit===1/0&&("function"!=typeof e||e===Pt)&&{index:n.index,range:n.range};return o(0).then(function(){if(0=i})).length?(t.forEach(function(e){c.push(function(){var t=l,n=e._cfg.dbschema;fn(r,t,a),fn(r,n,a),l=r._dbSchema=n;var u=un(t,n);u.add.forEach(function(e){sn(a,e[0],e[1].primKey,e[1].indexes)}),u.change.forEach(function(e){if(e.recreate)throw new V.Upgrade("Not yet support for changing primary key");var t=a.objectStore(e.name);e.add.forEach(function(e){return cn(t,e)}),e.change.forEach(function(e){t.deleteIndex(e.name),cn(t,e)}),e.del.forEach(function(e){return t.deleteIndex(e)})});var c=e._cfg.contentUpgrade;if(c&&e._cfg.version>i){Jt(r,a),s._memoizedTables={};var f=x(n);u.del.forEach(function(e){f[e]=t[e]}),tn(r,[r.Transaction.prototype]),en(r,[r.Transaction.prototype],o(f),f),s.schema=f;var h,d=R(c);return d&&Fe(),u=_e.follow(function(){var e;(h=c(s))&&d&&(e=Le.bind(null,null),h.then(e,e))}),h&&"function"==typeof h.then?_e.resolve(h):u.then(function(){return h})}}),c.push(function(t){var n,o,i=e._cfg.dbschema;n=i,o=t,[].slice.call(o.db.objectStoreNames).forEach(function(e){return null==n[e]&&o.db.deleteObjectStore(e)}),tn(r,[r.Transaction.prototype]),en(r,[r.Transaction.prototype],r._storeNames,r._dbSchema),s.schema=r._dbSchema}),c.push(function(t){r.idbdb.objectStoreNames.contains("$meta")&&(Math.ceil(r.idbdb.version/10)===e._cfg.version?(r.idbdb.deleteObjectStore("$meta"),delete r._dbSchema.$meta,r._storeNames=r._storeNames.filter(function(e){return"$meta"!==e})):t.objectStore("$meta").put(e._cfg.version,"version"))})}),function e(){return c.length?_e.resolve(c.shift()(s.idbtrans)).then(e):_e.resolve()}().then(function(){an(l,a)})):_e.resolve();var r,i,s,a,c,l}).catch(s)):(o(i).forEach(function(e){sn(n,e,i[e].primKey,i[e].indexes)}),Jt(e,n),void _e.follow(function(){return e.on.populate.fire(u)}).catch(s));var r,c})}function on(e,t){an(e._dbSchema,t),t.db.version%10!=0||t.objectStoreNames.contains("$meta")||t.db.createObjectStore("$meta").add(Math.ceil(t.db.version/10-1),"version");var n=ln(0,e.idbdb,t);fn(e,e._dbSchema,t);for(var r=0,o=un(n,e._dbSchema).change;rMath.pow(2,62)?0:r.oldVersion,h=r<1,e.idbdb=d.result,u&&on(e,f),rn(e,r/10,f,c))},c),d.onsuccess=Ke(function(){f=null;var n,s,c,p,y,m=e.idbdb=d.result,g=v(m.objectStoreNames);if(0t.limit?n.length=t.limit:e.length===t.limit&&n.length=r.limit&&(!r.values||e.req.values)&&Xn(e.req.query.range,r.query.range)}),!1,o,i];case"count":return u=i.find(function(e){return Gn(e.req.query.range,r.query.range)}),[u,!!u,o,i]}}(n,r,"query",e),a=s[0],c=s[1],l=s[2],f=s[3];return a&&c?a.obsSet=e.obsSet:(c=o.query(e).then(function(e){var n=e.result;if(a&&(a.res=n),t){for(var r=0,o=n.length;ri});var r=n(8896),o=n(1693);function i(e){return(0,o.T)(e[r.s])}},9283(e,t,n){"use strict";n.d(t,{C:()=>o});var r=n(5964);const o=(e,t,n)=>(0,r.ON)(e,t,n,r.C5)},9739(e,t,n){"use strict";n.d(t,{X:()=>r});var r=function(e){return e&&"number"==typeof e.length&&"function"!=typeof e}},9852(e,t,n){"use strict";n.d(t,{H:()=>_});var r=n(7688),o=n(8071),i=n(5096),u=n(4370);function s(e,t){return void 0===t&&(t=0),(0,i.N)(function(n,r){n.subscribe((0,u._)(r,function(n){return(0,o.N)(r,e,function(){return r.next(n)},t)},function(){return(0,o.N)(r,e,function(){return r.complete()},t)},function(n){return(0,o.N)(r,e,function(){return r.error(n)},t)}))})}function a(e,t){return void 0===t&&(t=0),(0,i.N)(function(n,r){r.add(e.schedule(function(){return n.subscribe(r)},t))})}var c=n(1753);var l=n(4207),f=n(1693);function h(e,t){if(!e)throw new Error("Iterable cannot be null");return new c.c(function(n){(0,o.N)(n,t,function(){var r=e[Symbol.asyncIterator]();(0,o.N)(n,t,function(){r.next().then(function(e){e.done?n.complete():n.next(e.value)})},0,!0)})})}var d=n(9065),p=n(5796),y=n(9739),v=n(4799),m=n(3819),g=n(4429),b=n(3026);function w(e,t){if(null!=e){if((0,d.l)(e))return function(e,t){return(0,r.Tg)(e).pipe(a(t),s(t))}(e,t);if((0,y.X)(e))return function(e,t){return new c.c(function(n){var r=0;return t.schedule(function(){r===e.length?n.complete():(n.next(e[r++]),n.closed||this.schedule())})})}(e,t);if((0,p.y)(e))return function(e,t){return(0,r.Tg)(e).pipe(a(t),s(t))}(e,t);if((0,m.T)(e))return h(e,t);if((0,v.x)(e))return function(e,t){return new c.c(function(n){var r;return(0,o.N)(n,t,function(){r=e[l.l](),(0,o.N)(n,t,function(){var e,t,o;try{t=(e=r.next()).value,o=e.done}catch(i){return void n.error(i)}o?n.complete():n.next(t)},0,!0)}),function(){return(0,f.T)(null==r?void 0:r.return)&&r.return()}})}(e,t);if((0,b.U)(e))return function(e,t){return h((0,b.C)(e),t)}(e,t)}throw(0,g.L)(e)}function _(e,t){return t?w(e,t):(0,r.Tg)(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/9dae6e71.d38c8ec6.js b/docs/assets/js/9dae6e71.d38c8ec6.js
deleted file mode 100644
index 34c6b9d5ff2..00000000000
--- a/docs/assets/js/9dae6e71.d38c8ec6.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3325],{2231(e,r,t){t.r(r),t.d(r,{default:()=>l});var a=t(544),i=t(4848);function l(){return(0,a.default)({sem:{id:"gads",metaTitle:"The Open Source alternative for Firestore",title:(0,i.jsxs)(i.Fragment,{children:["The ",(0,i.jsx)("b",{children:"Open Source"})," alternative for "," ",(0,i.jsx)("b",{children:"Firestore"})]})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/9dd8ea89.9d935eb6.js b/docs/assets/js/9dd8ea89.9d935eb6.js
deleted file mode 100644
index 34ca75fc383..00000000000
--- a/docs/assets/js/9dd8ea89.9d935eb6.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1715],{7775(e,n,s){s.r(n),s.d(n,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"logger","title":"RxDB Logger Plugin - Track & Optimize","description":"Take control of your RxDatabase logs. Monitor every write, query, or attachment retrieval to swiftly diagnose and fix performance bottlenecks.","source":"@site/docs/logger.md","sourceDirName":".","slug":"/logger.html","permalink":"/logger.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB Logger Plugin - Track & Optimize","slug":"logger.html","description":"Take control of your RxDatabase logs. Monitor every write, query, or attachment retrieval to swiftly diagnose and fix performance bottlenecks."},"sidebar":"tutorialSidebar","previous":{"title":"Key Compression","permalink":"/key-compression.html"},"next":{"title":"Remote RxStorage","permalink":"/rx-storage-remote.html"}}');var o=s(4848),i=s(8453);const a={title:"RxDB Logger Plugin - Track & Optimize",slug:"logger.html",description:"Take control of your RxDatabase logs. Monitor every write, query, or attachment retrieval to swiftly diagnose and fix performance bottlenecks."},l="RxDB Logger Plugin",t={},c=[{value:"Using the logger plugin",id:"using-the-logger-plugin",level:2},{value:"Specify what to be logged",id:"specify-what-to-be-logged",level:2},{value:"Using custom logging functions",id:"using-custom-logging-functions",level:2}];function d(e){const n={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"rxdb-logger-plugin",children:"RxDB Logger Plugin"})}),"\n",(0,o.jsxs)(n.p,{children:["With the logger plugin you can log all operations to the ",(0,o.jsx)(n.a,{href:"/rx-storage.html",children:"storage layer"})," of your ",(0,o.jsx)(n.a,{href:"/rx-database.html",children:"RxDatabase"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["This is useful to debug performance problems and for monitoring with Application Performance Monitoring (APM) tools like ",(0,o.jsx)(n.strong,{children:"Bugsnag"}),", ",(0,o.jsx)(n.strong,{children:"Datadog"}),", ",(0,o.jsx)(n.strong,{children:"Elastic"}),", ",(0,o.jsx)(n.strong,{children:"Sentry"})," and others."]}),"\n",(0,o.jsxs)(n.p,{children:["Notice that the logger plugin is not part of the RxDB core, it is part of ",(0,o.jsx)(n.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"}),"."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/logger.png",alt:"RxDB logger example",width:"600px"})}),"\n",(0,o.jsx)(n.h2,{id:"using-the-logger-plugin",children:"Using the logger plugin"}),"\n",(0,o.jsxs)(n.p,{children:["The logger is a wrapper that can be wrapped around any ",(0,o.jsx)(n.a,{href:"/rx-storage.html",children:"RxStorage"}),". Once your storage is wrapped, you can create your database with the wrapped storage and the logging will automatically happen."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" wrappedLoggerStorage"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/logger'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// wrap a storage with the logger"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" loggingStorage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedLoggerStorage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({})"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// create your database with the wrapped storage"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" loggingStorage"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// create collections etc..."})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"specify-what-to-be-logged",children:"Specify what to be logged"}),"\n",(0,o.jsxs)(n.p,{children:["By default, the plugin will log all operations and it will also run a ",(0,o.jsx)(n.code,{children:"console.time()/console.timeEnd()"})," around each operation. You can specify what to log so that your logs are less noisy. For this you provide a settings object when calling ",(0,o.jsx)(n.code,{children:"wrappedLoggerStorage()"}),"."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" loggingStorage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedLoggerStorage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({})"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" settings"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // can used to prefix all log strings, default=''"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" prefix"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-prefix'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Be default, all settings are true."})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // if true, it will log timings with console.time() and console.timeEnd()"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" times"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // if false, it will not log meta storage instances like used in replication"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" metaStorageInstances"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // operations"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" bulkWrite"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" findDocumentsById"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" query"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" count"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" info"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getAttachmentData"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getChangedDocumentsSince"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" cleanup"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" close"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" remove"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,o.jsx)(n.h2,{id:"using-custom-logging-functions",children:"Using custom logging functions"}),"\n",(0,o.jsx)(n.p,{children:"With the logger plugin you can also run custom log functions for all operations."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" loggingStorage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedLoggerStorage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({})"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" onOperationStart"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (operationsName"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" logId"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" args) "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" void"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" onOperationEnd"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (operationsName"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" logId"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" args) "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" void"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" onOperationError"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (operationsName"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" logId"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" args"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" error) "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" void"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>a,x:()=>l});var r=s(6540);const o={},i=r.createContext(o);function a(e){const n=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:a(e.components),r.createElement(i.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/9e91b6f0.cfa4288f.js b/docs/assets/js/9e91b6f0.cfa4288f.js
deleted file mode 100644
index 756c8d98e74..00000000000
--- a/docs/assets/js/9e91b6f0.cfa4288f.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3021],{5200(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>r,default:()=>d,frontMatter:()=>i,metadata:()=>s,toc:()=>h});const s=JSON.parse('{"id":"why-nosql","title":"Why NoSQL Powers Modern UI Apps","description":"Discover how NoSQL enables offline-first UI apps. Learn about easy replication, conflict resolution, and why relational data isn\'t always necessary.","source":"@site/docs/why-nosql.md","sourceDirName":".","slug":"/why-nosql.html","permalink":"/why-nosql.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Why NoSQL Powers Modern UI Apps","slug":"why-nosql.html","description":"Discover how NoSQL enables offline-first UI apps. Learn about easy replication, conflict resolution, and why relational data isn\'t always necessary."},"sidebar":"tutorialSidebar","previous":{"title":"Why Local-First Software Is the Future and its Limitations","permalink":"/articles/local-first-future.html"},"next":{"title":"Downsides of Local First / Offline First","permalink":"/downsides-of-offline-first.html"}}');var o=t(4848),a=t(8453);t(2271);const i={title:"Why NoSQL Powers Modern UI Apps",slug:"why-nosql.html",description:"Discover how NoSQL enables offline-first UI apps. Learn about easy replication, conflict resolution, and why relational data isn't always necessary."},r="Why UI applications need NoSQL",l={},h=[{value:"Transactions do not work with humans involved",id:"transactions-do-not-work-with-humans-involved",level:2},{value:"Transactions do not work with offline-first",id:"transactions-do-not-work-with-offline-first",level:2},{value:"Relational queries in NoSQL",id:"relational-queries-in-nosql",level:2},{value:"Reliable replication",id:"reliable-replication",level:2},{value:"Server side validation",id:"server-side-validation",level:2},{value:"Event optimization",id:"event-optimization",level:2},{value:"Migration without relations",id:"migration-without-relations",level:2},{value:"Everything can be downgraded to NoSQL",id:"everything-can-be-downgraded-to-nosql",level:2},{value:"Caching query results",id:"caching-query-results",level:2},{value:"TypeScript support",id:"typescript-support",level:2},{value:"What you lose with NoSQL",id:"what-you-lose-with-nosql",level:2},{value:"But there is database XY",id:"but-there-is-database-xy",level:2}];function c(e){const n={a:"a",blockquote:"blockquote",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.header,{children:(0,o.jsx)(n.h1,{id:"why-ui-applications-need-nosql",children:"Why UI applications need NoSQL"})}),"\n",(0,o.jsxs)(n.p,{children:[(0,o.jsx)(n.a,{href:"https://rxdb.info",children:"RxDB"}),", a client side, offline first, JavaScript database, is now several years old.\nOften new users appear in the chat and ask for that one simple feature:\nThey want to store and query ",(0,o.jsx)(n.strong,{children:"relational data"}),"."]}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsx)(n.p,{children:"So why not just implement SQL?"}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["All these client databases out there have on some kind of document based, NoSQL like, storage engine. PouchDB, Firebase, AWS Datastore, RethinkDB's ",(0,o.jsx)(n.a,{href:"https://github.com/rethinkdb/horizon",children:"Horizon"}),", Meteor's ",(0,o.jsx)(n.a,{href:"https://github.com/mWater/minimongo",children:"Minimongo"}),", ",(0,o.jsx)(n.a,{href:"https://parseplatform.org/",children:"Parse"}),", ",(0,o.jsx)(n.a,{href:"https://realm.io/",children:"Realm"}),". They all do not have real relational data."]}),"\n",(0,o.jsxs)(n.p,{children:["They might have some kind of weak relational foreign keys like the ",(0,o.jsx)(n.a,{href:"/population.html",children:"RxDB Population"}),"\nor the ",(0,o.jsx)(n.a,{href:"https://docs.amplify.aws/lib/datastore/relational/q/platform/js/",children:"relational models"})," of AWS Datastore.\nBut these relations are weak. The foreign keys are not enforced to be valid like in PostgreSQL, and you cannot query\nthe rows with complex subqueries over different tables or collections and then make mutations based on the result."]}),"\n",(0,o.jsx)(n.p,{children:"There must be a reason for that. In fact, there are multiple of them and in the following I want to show you why you can neither have, nor want real relational data when you have a client-side database with replication."}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/no-sql.png",alt:"NoSQL",width:"100"})}),"\n",(0,o.jsx)(n.h2,{id:"transactions-do-not-work-with-humans-involved",children:"Transactions do not work with humans involved"}),"\n",(0,o.jsxs)(n.p,{children:["On the server side, transactions are used to run steps of logic inside of a self contained ",(0,o.jsx)(n.code,{children:"unit of work"}),". The database system ensures that multiple transactions do not run in parallel or interfere with each other.\nThis works well because on the server side you can predict how longer everything takes. It can be ensured that one transaction does not block everything else for too long which would make the system not responding anymore to other requests."]}),"\n",(0,o.jsx)(n.p,{children:"When you build a UI based application that is used by a real human, you can no longer predict how long anything takes.\nThe user clicks the edit button and expects to not have anyone else change the document while the user is in edit mode.\nUsing a transaction to ensure nothing is changed in between, is not an option because the transaction could be open for a long\ntime and other background tasks, like replication, would no longer work."}),"\n",(0,o.jsxs)(n.p,{children:["So whenever a human is involved, this kind of logic has to be implemented using other strategies. Most NoSQL databases like ",(0,o.jsx)(n.a,{href:"./",children:"RxDB"})," or ",(0,o.jsx)(n.a,{href:"/replication-couchdb.html",children:"CouchDB"})," use a system based on ",(0,o.jsx)(n.a,{href:"/transactions-conflicts-revisions.html",children:"revision and conflicts"})," to handle these."]}),"\n",(0,o.jsx)(n.h2,{id:"transactions-do-not-work-with-offline-first",children:"Transactions do not work with offline-first"}),"\n",(0,o.jsxs)(n.p,{children:["When you want to build an ",(0,o.jsx)(n.a,{href:"/offline-first.html",children:"offline-first"})," application, it is assumed that the user can also read and write data, even when the device has lost the connection to the backend.\nYou could use database transactions on writes to the client's database state, but enforcing a transaction boundary across other instances like clients or servers, is not possible when there is no connection."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/why-no-transactions.jpg",alt:"offline first vs relational transactions",width:"400"})}),"\n",(0,o.jsxs)(n.p,{children:["On the client you could run an update query where all ",(0,o.jsx)(n.code,{children:"color: red"})," rows are changed to ",(0,o.jsx)(n.code,{children:"color: blue"}),", but this would not guarantee that there will still be other ",(0,o.jsx)(n.code,{children:"red"})," documents when the client goes online again and restarts the replication with the server."]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"sql","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"sql","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"UPDATE"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"SET"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs.color "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'red'"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"WHERE"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs.color "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'blue'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),"\n",(0,o.jsx)(n.h2,{id:"relational-queries-in-nosql",children:"Relational queries in NoSQL"}),"\n",(0,o.jsx)(n.p,{children:"What most people want from a relational database, is to run queries over multiple tables.\nSome people think that they cannot do that with NoSQL, so let me explain."}),"\n",(0,o.jsxs)(n.p,{children:["Let's say you have two tables with ",(0,o.jsx)(n.code,{children:"customers"})," and ",(0,o.jsx)(n.code,{children:"cities"})," where each city has an ",(0,o.jsx)(n.code,{children:"id"})," and each customer has a ",(0,o.jsx)(n.code,{children:"city_id"}),". You want to get every customer that resides in ",(0,o.jsx)(n.code,{children:"Tokyo"}),". With SQL, you would use a query like this:"]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"sql","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"sql","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"SELECT"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"FROM"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" city"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"WHERE"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" city.name "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Tokyo'"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"LEFT JOIN"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" customer "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"ON"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" customer.city_id "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" city.id;"})]})]})})}),"\n",(0,o.jsxs)(n.p,{children:["With ",(0,o.jsx)(n.strong,{children:"NoSQL"})," you can just do the same, but you have to write it manually:"]}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"typescript","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"typescript","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" cityDocument"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"cities"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".where"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'name'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".equals"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Tokyo'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" customerDocuments"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"customers"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".where"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'city_id'"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".equals"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"cityDocument"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id)"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,o.jsx)(n.p,{children:"So what are the differences? The SQL version would run faster on a remote database server because it would aggregate all data there and return only the customers as result set. But when you have a local database, it is not really a difference. Querying the two tables by hand would have about the same performance as a JavaScript implementation of SQL that is running locally."}),"\n",(0,o.jsxs)(n.p,{children:["The main benefit from using SQL is, that the SQL query runs inside of a ",(0,o.jsx)(n.strong,{children:"single transaction"}),". When a change to one of our two tables happens, while our query runs, the SQL database will ensure that the write does not affect the result of the query. This could happen with NoSQL, while you retrieve the city document, the customer table gets changed and your result is not correct for the dataset that was there when you started the querying. As a workaround, you could observe the database for changes and if a change happened in between, you have to re-run everything."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/no-relational-data.png",alt:"no relational data",width:"250"})}),"\n",(0,o.jsx)(n.h2,{id:"reliable-replication",children:"Reliable replication"}),"\n",(0,o.jsxs)(n.p,{children:["In an offline first app, your data is replicated from your backend servers to your users and you want it to be reliable.\nThe replication is ",(0,o.jsx)(n.strong,{children:"reliable"})," when, no matter what happens, every online client is able to run a replication\nand end up with the ",(0,o.jsx)(n.strong,{children:"exact same"})," database state as any other client."]}),"\n",(0,o.jsx)(n.p,{children:"Implementing a reliable replication protocol is hard because of the circumstances of your app:"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"Your users have unknown devices."}),"\n",(0,o.jsx)(n.li,{children:"They have an unknown internet speed."}),"\n",(0,o.jsx)(n.li,{children:"They can go offline or online at any time."}),"\n",(0,o.jsx)(n.li,{children:"Clients can be offline for a several days with un-synced changes."}),"\n",(0,o.jsx)(n.li,{children:"You can have many users at the same time."}),"\n",(0,o.jsx)(n.li,{children:"The users can do many database writes at the same time to the same entities."}),"\n"]}),"\n",(0,o.jsx)(n.p,{children:"Now lets say you have a SQL database and one of your users, called Alice, runs a query that mutates some rows, based on a condition."}),"\n",(0,o.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"sql","data-theme":"css-variables",children:(0,o.jsxs)(n.code,{"data-language":"sql","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"# mark all items "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"out"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" of stock "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" inStock"}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"FALSE"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"UPDATE"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Table_A"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"SET"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Table_A.inStock "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" FALSE"})]}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"FROM"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Table_A"})}),"\n",(0,o.jsx)(n.span,{"data-line":"",children:(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"WHERE"})}),"\n",(0,o.jsxs)(n.span,{"data-line":"",children:[(0,o.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Table_A.amountInStock "}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"})]})]})})}),"\n",(0,o.jsx)(n.p,{children:"At first, the query runs on the local database of Alice and everything is fine."}),"\n",(0,o.jsxs)(n.p,{children:["But at the same time Bob, the other client, updates a row and sets ",(0,o.jsx)(n.code,{children:"amountInStock"})," from ",(0,o.jsx)(n.code,{children:"0"})," to ",(0,o.jsx)(n.code,{children:"1"}),".\nNow Bob's client replicates the changes from Alice and runs them. Bob will end up with a different database state than Alice because on one of the rows, the ",(0,o.jsx)(n.code,{children:"WHERE"})," condition was not met. This is not what we want, so our replication protocol should be able to fix it. For that it has to reduce all mutations into a deterministic state."]}),"\n",(0,o.jsx)(n.p,{children:'Let me loosely describe how "many" SQL replications work:'}),"\n",(0,o.jsxs)(n.p,{children:["Instead of just running all replicated queries, we remember a list of all past queries. When a new query comes in that happened ",(0,o.jsx)(n.code,{children:"before"})," our last query, we roll back the previous queries, run the new query, and then re-execute our own queries on top of that. For that to work, all queries need a timestamp so we can order them correctly. But you cannot rely on the clock that is running at the client. Client side clocks drift, they can run in a different speed or even a malicious client modifies the clock on purpose. So instead of a normal timestamp, we have to use a ",(0,o.jsx)(n.a,{href:"https://jaredforsyth.com/posts/hybrid-logical-clocks/",children:"Hybrid Logical Clock"})," that takes a client generated id and the number of the clients query into account. Our timestamp will then look like ",(0,o.jsx)(n.code,{children:"2021-10-04T15:29.40.273Z-0000-eede1195b7d94dd5"}),". These timestamps can be brought into a deterministic order and each client can run the replicated queries in the same order."]}),"\n",(0,o.jsx)(n.p,{children:"While this sounds easy and realizable, we have some problems:\nThis kind of replication works great when you replicate between multiple SQL servers. It does not work great when you replicate between a single server and many clients."}),"\n",(0,o.jsxs)(n.ol,{children:["\n",(0,o.jsx)(n.li,{children:"As mentioned above, clients can be offline for a long time which could require us to do many and heavy rollbacks on each client when someone comes back after a long time and replicates the change."}),"\n",(0,o.jsx)(n.li,{children:"We have many clients where many changes can appear and our database would have to roll back many times."}),"\n",(0,o.jsx)(n.li,{children:"During the rollback, the database cannot be used for read queries."}),"\n",(0,o.jsx)(n.li,{children:"It is required that each client downloads and keeps the whole query history."}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["With ",(0,o.jsx)(n.strong,{children:"NoSQL"}),", replication works different. A new client downloads all current documents and each time a document changes, that document is downloaded again. Instead of replicating the query that leads to a data change, we just replicate the changed data itself. Of course, we could do the same with SQL and just replicate the affected rows of a query, like WatermelonDB ",(0,o.jsx)(n.a,{href:"https://youtu.be/uFvHURTRLxQ?t=1133",children:"does it"}),". This was a clever way to go for WatermelonDB, because it was initially made for React Native and did want to use the fast SQLite instead of the slow ",(0,o.jsx)(n.a,{href:"https://medium.com/@Sendbird/extreme-optimization-of-asyncstorage-in-react-native-b2a1e0107b34",children:"AsyncStorage"}),". But in a more general view, it defeats the whole purpose of having a replicating relational database because you have transactions locally, but these transactions become ",(0,o.jsx)(n.strong,{children:"meaningless"})," as soon as the data goes through the replication layer."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/database-replication.png",alt:"database replication",width:"200"})}),"\n",(0,o.jsx)(n.h2,{id:"server-side-validation",children:"Server side validation"}),"\n",(0,o.jsx)(n.p,{children:"Whenever there is client-side input, it must be validated on the server.\nOn a NoSQL database, validating a changed document is trivial. The client sends the changed document to the server, and the server can then check if the user was allowed to modify that one document and if the applied changes are ok."}),"\n",(0,o.jsx)(n.p,{children:"Safely validating a SQL query is up to impossible."}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"You first need a way to parse the query with all this complex SQL syntax and keywords."}),"\n",(0,o.jsx)(n.li,{children:"You have to ensure that the query does not DOS your system."}),"\n",(0,o.jsx)(n.li,{children:"Then you check which rows would be affected when running the query and if the user was allowed to change them"}),"\n",(0,o.jsx)(n.li,{children:"Then you check if the mutation to that rows are valid."}),"\n"]}),"\n",(0,o.jsxs)(n.p,{children:["For simple queries like an insert/update/delete to a single row, this might be doable. But a query with 4 ",(0,o.jsx)(n.code,{children:"LEFT JOIN"})," will be hard."]}),"\n",(0,o.jsx)(n.h2,{id:"event-optimization",children:"Event optimization"}),"\n",(0,o.jsx)(n.p,{children:"With NoSQL databases, each write event always affects exactly one document. This makes it easy to optimize the processing of events at the client. For example instead of handling multiple updates to the same document, when the user comes online again, you could skip everything but the last event."}),"\n",(0,o.jsxs)(n.p,{children:["Similar to that you can optimize observable query results. When you query the ",(0,o.jsx)(n.code,{children:"customers"})," table you get a query result of 10 customers. Now a new customer is added to the table and you want to know how the new query results look like. You could analyze the event and now you know that you only have to add the new customer to the previous results set, instead of running the whole query again. These types of optimizations can be run with all NoSQL queries and even work with ",(0,o.jsx)(n.code,{children:"limit"})," and ",(0,o.jsx)(n.code,{children:"skip"})," operators. In RxDB this all happens in the background with the ",(0,o.jsx)(n.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce algorithm"})," that calculates new query results on incoming changes."]}),"\n",(0,o.jsx)(n.p,{children:"These optimizations do not really work with relational data. A change to one table could affect a query to any other tables. and you could not just calculate the new results based on the event. You would always have to re-run the full query to get the updated results."}),"\n",(0,o.jsx)(n.h2,{id:"migration-without-relations",children:"Migration without relations"}),"\n",(0,o.jsx)(n.p,{children:"Sooner or later you change the layout of your data. You update the schema and you also have to migrate the stored rows/documents. In NoSQL this is often not a big deal because all of your documents are modeled as self containing piece of data. There is an old version of the document and you have a function that transforms it into the new version."}),"\n",(0,o.jsx)(n.p,{children:"With relational data, nothing is self-contained. The relevant data for the migration of a single row could be inside any other table. So when changing the schema, it will be important which table to migrate first and how to orchestrate the migration or relations."}),"\n",(0,o.jsx)(n.p,{children:"On client side applications, this is even harder because the client can close the application at any time and the migration must be able to continue."}),"\n",(0,o.jsx)(n.h2,{id:"everything-can-be-downgraded-to-nosql",children:"Everything can be downgraded to NoSQL"}),"\n",(0,o.jsxs)(n.p,{children:["To use an offline first database in the frontend, you have to make it compatible with your backend APIs.\nMaking software things compatible often means you have to find the ",(0,o.jsx)(n.strong,{children:"lowest common denominator"}),".\nWhen you have SQLite in the frontend and want to replicate it with the backend, the backend also has to use SQLite. You cannot even use PostgreSQL because it has a different SQL dialect and some queries might fail. But you do not want to let the frontend dictate which technologies to use in the backend just to make replication work."]}),"\n",(0,o.jsxs)(n.p,{children:["With NoSQL, you just have documents and writes to these documents. You can build a document based layer on top of everything by ",(0,o.jsx)(n.strong,{children:"removing"})," functionality. It can be built on top of SQL, but also on top of a graph database or even on top of a key-value store like ",(0,o.jsx)(n.a,{href:"/adapters.html#leveldown",children:"levelDB"})," or ",(0,o.jsx)(n.a,{href:"/rx-storage-foundationdb.html",children:"FoundationDB"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["With that document layer you can build a ",(0,o.jsx)(n.a,{href:"/replication.html",children:"Sync Engine"})," that serves documents sorted by the last update time and there you have a realtime replication."]}),"\n",(0,o.jsx)(n.h2,{id:"caching-query-results",children:"Caching query results"}),"\n",(0,o.jsx)(n.p,{children:"Memory is limited and this is especially true for client side applications where you never know how much free RAM the device really has. You want to have a fast realtime UI, so your database must be able to cache query results."}),"\n",(0,o.jsxs)(n.p,{children:["When you run a SQL query like ",(0,o.jsx)(n.code,{children:"SELECT .."})," the result of it can be anything. An ",(0,o.jsx)(n.code,{children:"array"}),", a ",(0,o.jsx)(n.code,{children:"number"}),", a ",(0,o.jsx)(n.code,{children:"string"}),", a single row, it depends on how the query goes on. So the caching strategy can only be to keep the result in memory, once for each query.\nThis scales very bad because the more queries you run, the more results you have to store in memory."]}),"\n",(0,o.jsxs)(n.p,{children:["When you make a query to a NoSQL collection, you always know how the result will look like. It is a list of documents, based on the collection's schema (if you have one). The result set is stored in memory, but because you get similar documents for different queries to the same collection, we can de-duplicated the documents. So when multiple queries return the same document, we only have it in the cache ",(0,o.jsx)(n.strong,{children:"once"})," and each query caches point to the same memory object. So no matter how many queries you make, your cache maximum is the collection size."]}),"\n",(0,o.jsx)(n.h2,{id:"typescript-support",children:"TypeScript support"}),"\n",(0,o.jsx)(n.p,{children:"Modern web apps are build with TypeScript and you want the transpiler to know the types of your query result so it can give you build time errors when something does not match. This is quite easy on document based systems. The typings of for each document of a collection can be generated from the schema, and all queries to that collection will always return the given document type. With SQL you have to manually write the typings for each query by hand because it can contain all these aggregate functions that affect the type of the query's result."}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/typescript.png",alt:"typescript",width:"80"})}),"\n",(0,o.jsx)(n.h2,{id:"what-you-lose-with-nosql",children:"What you lose with NoSQL"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsx)(n.li,{children:"You can not run relational queries across tables inside a single transaction."}),"\n",(0,o.jsxs)(n.li,{children:["You can not mutate documents based on a ",(0,o.jsx)(n.code,{children:"WHERE"})," clause, in a single transaction."]}),"\n",(0,o.jsx)(n.li,{children:"You need to resolve replication conflicts on a per-document basis."}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"but-there-is-database-xy",children:"But there is database XY"}),"\n",(0,o.jsx)(n.p,{children:"Yes, there are SQL databases out there that run on the client side or have replication, but not both."}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["WebSQL / ",(0,o.jsx)(n.a,{href:"https://github.com/sql-js/sql.js/",children:"sql.js"}),": In the past there was ",(0,o.jsx)(n.strong,{children:"WebSQL"})," in the browser. It was a direct mapping to SQLite because all browsers used the SQLite implementation. You could store relational data in it, but there was no concept of replication at any point in time. ",(0,o.jsx)(n.strong,{children:"sql.js"})," is an SQLite complied to JavaScript. It has not replication and it has (for now) no persistent storage, everything is stored in memory."]}),"\n",(0,o.jsx)(n.li,{children:"WatermelonDB is a SQL databases that runs in the client. WatermelonDB uses a document-based replication that is not able to replicate relational queries."}),"\n",(0,o.jsx)(n.li,{children:"Cockroach / Spanner/ PostgreSQL etc. are SQL databases with replication. But they run on servers, not on clients, so they can make different trade offs."}),"\n"]}),"\n",(0,o.jsx)(n.h1,{id:"further-read",children:"Further read"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsxs)(n.p,{children:["Cockroach Labs: ",(0,o.jsx)(n.a,{href:"https://www.cockroachlabs.com/blog/living-without-atomic-clocks/",children:"Living Without Atomic Clocks"})]}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.a,{href:"/transactions-conflicts-revisions.html",children:"Transactions, Conflicts and Revisions in RxDB"})}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.a,{href:"https://dbmsmusings.blogspot.com/2015/10/why-mongodb-cassandra-hbase-dynamodb_28.html",children:"Why MongoDB, Cassandra, HBase, DynamoDB, and Riak will only let you perform transactions on a single data item"})}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:(0,o.jsx)(n.code,{children:"Make a PR to this file if you have more interesting links to that topic"})}),"\n"]}),"\n"]})]})}function d(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(c,{...e})}):c(e)}},8453(e,n,t){t.d(n,{R:()=>i,x:()=>r});var s=t(6540);const o={},a=s.createContext(o);function i(e){const n=s.useContext(a);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),s.createElement(a.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a406dc27.d08b03b3.js b/docs/assets/js/a406dc27.d08b03b3.js
deleted file mode 100644
index 15d02662ac5..00000000000
--- a/docs/assets/js/a406dc27.d08b03b3.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1500],{6837(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"migration-storage","title":"Migration Storage","description":"Effortlessly migrate your data between storages in RxDB using the Storage Migration plugin. Retain your documents when switching storages or major versions.","source":"@site/docs/migration-storage.md","sourceDirName":".","slug":"/migration-storage.html","permalink":"/migration-storage.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Migration Storage","slug":"migration-storage.html","description":"Effortlessly migrate your data between storages in RxDB using the Storage Migration plugin. Retain your documents when switching storages or major versions."},"sidebar":"tutorialSidebar","previous":{"title":"Schema Migration","permalink":"/migration-schema.html"},"next":{"title":"Attachments","permalink":"/rx-attachment.html"}}');var i=n(4848),o=n(8453);const a={title:"Migration Storage",slug:"migration-storage.html",description:"Effortlessly migrate your data between storages in RxDB using the Storage Migration plugin. Retain your documents when switching storages or major versions."},l="Storage Migration",t={},c=[{value:"Usage",id:"usage",level:2},{value:"Migrate from a previous RxDB major version",id:"migrate-from-a-previous-rxdb-major-version",level:2},{value:"Disable Version Check on RxDB Premium \ud83d\udc51",id:"disable-version-check-on-rxdb-premium-",level:2}];function d(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"storage-migration",children:"Storage Migration"})}),"\n",(0,i.jsx)(s.p,{children:"The storage migration plugin can be used to migrate all data from one existing RxStorage into another. This is useful when:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["You want to migrate from one ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"})," to another one."]}),"\n",(0,i.jsx)(s.li,{children:"You want to migrate to a new major RxDB version while keeping the previous saved data. This function only works from the previous major version upwards. Do not use it to migrate like rxdb v9 to v14."}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["The storage migration ",(0,i.jsx)(s.strong,{children:"drops deleted documents"})," and filters them out during the migration."]}),"\n",(0,i.jsxs)(s.admonition,{title:"Do never change the schema while doing a storage migration",type:"warning",children:[(0,i.jsx)(s.p,{children:"When you migrate between storages, you might want to change the schema in the same process. You should never do that because it will lead to problems afterwards and might make your database unusable."}),(0,i.jsxs)(s.p,{children:["When you also want to change your schema, first run the storage migration and afterwards run a normal ",(0,i.jsx)(s.a,{href:"/migration-schema.html",children:"schema migration"}),"."]})]}),"\n",(0,i.jsx)(s.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(s.p,{children:["Lets say you want to migrate from ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"})," to the ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),"."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { migrateStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/migration-storage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-old/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create the new RxDatabase"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" dbLocation"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" migrateStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Name of the old database,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * using the storage migration requires that the"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * new database has a different name."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" oldDatabaseName"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myOldDatabaseName'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" oldStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // RxStorage of the old database"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 500"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // batch size"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" parallel"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- true if it should migrate all collections in parallel. False (default) if should migrate in serial"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" afterMigrateBatch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (input"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" AfterMigrateBatchHandlerInput"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'storage migration: batch processed'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.admonition,{type:"note",children:[(0,i.jsx)(s.p,{children:"Only collections that exist in the new database at the time you call migrateStorage() will have their data migrated."}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["If your old database had collections ",(0,i.jsx)(s.code,{children:"['users', 'posts', 'comments']"})," but your new database only defines ",(0,i.jsx)(s.code,{children:"['users', 'posts']"}),", then only users and posts data will be migrated."]}),"\n",(0,i.jsx)(s.li,{children:"Any collections missing from the new database will simply be skipped - no data for them will be read or written."}),"\n"]}),(0,i.jsxs)(s.p,{children:["This allows you to selectively migrate only certain collections if desired, by choosing which collections to define before invoking ",(0,i.jsx)(s.code,{children:"migrateStorage()"}),"."]})]}),"\n",(0,i.jsx)(s.h2,{id:"migrate-from-a-previous-rxdb-major-version",children:"Migrate from a previous RxDB major version"}),"\n",(0,i.jsxs)(s.p,{children:["To migrate from a previous RxDB major version, you have to install the 'old' RxDB in the ",(0,i.jsx)(s.code,{children:"package.json"})]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"json","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"json","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "dependencies"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:' "rxdb-old"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "npm:rxdb@14.17.1"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"Then you can run the migration by providing the old storage:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { migrateStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/migration-storage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-old/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"; "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// <- import from the old RxDB version"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" migrateStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Name of the old database,"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * using the storage migration requires that the"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * new database has a different name."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" oldDatabaseName"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myOldDatabaseName'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" oldStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // RxStorage of the old database"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 500"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // batch size"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" parallel"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" afterMigrateBatch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (input"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" AfterMigrateBatchHandlerInput"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'storage migration: batch processed'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"})})]})})}),"\n",(0,i.jsxs)(s.h2,{id:"disable-version-check-on-rxdb-premium-",children:["Disable Version Check on ",(0,i.jsx)(s.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"})]}),"\n",(0,i.jsxs)(s.p,{children:["RxDB Premium has a check in place that ensures that you do not accidentally use the wrong RxDB core and \ud83d\udc51 Premium version together which could break your database state.\nThis can be a problem during migrations where you have multiple versions of RxDB in use and it will throw the error ",(0,i.jsx)(s.code,{children:"Version mismatch detected"}),".\nYou can disable that check by importing and running the ",(0,i.jsx)(s.code,{children:"disableVersionCheck()"})," function from RxDB Premium."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// RxDB Premium v15 or newer:"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" disableVersionCheck"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium-old/plugins/shared'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"disableVersionCheck"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// RxDB Premium v14:"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// for esm"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" disableVersionCheck"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium-old/dist/es/shared/version-check.js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"disableVersionCheck"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// for cjs"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" disableVersionCheck"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium-old/dist/lib/shared/version-check.js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"disableVersionCheck"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a442adcd.3011bc9b.js b/docs/assets/js/a442adcd.3011bc9b.js
deleted file mode 100644
index 49f18c18582..00000000000
--- a/docs/assets/js/a442adcd.3011bc9b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8760],{3247(e,s,n){n.d(s,{g:()=>i});var r=n(4848);function i(e){const s=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&s.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&s.push(n),(0,r.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:s+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,r.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5551(e,s,n){n.r(s),n.d(s,{assets:()=>d,contentTitle:()=>c,default:()=>k,frontMatter:()=>t,metadata:()=>r,toc:()=>h});const r=JSON.parse('{"id":"reactivity","title":"Signals & Custom Reactivity with RxDB","description":"Level up reactivity with Angular signals, Vue refs, or Preact signals in RxDB. Learn how to integrate custom reactivity to power your dynamic UI.","source":"@site/docs/reactivity.md","sourceDirName":".","slug":"/reactivity.html","permalink":"/reactivity.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Signals & Custom Reactivity with RxDB","slug":"reactivity.html","description":"Level up reactivity with Angular signals, Vue refs, or Preact signals in RxDB. Learn how to integrate custom reactivity to power your dynamic UI."},"sidebar":"tutorialSidebar","previous":{"title":"RxPipelines","permalink":"/rx-pipeline.html"},"next":{"title":"RxState","permalink":"/rx-state.html"}}');var i=n(4848),o=n(8453),a=n(2636),l=n(3247);const t={title:"Signals & Custom Reactivity with RxDB",slug:"reactivity.html",description:"Level up reactivity with Angular signals, Vue refs, or Preact signals in RxDB. Learn how to integrate custom reactivity to power your dynamic UI."},c="Signals & Co. - Custom reactivity adapters instead of RxJS Observables",d={},h=[{value:"Adding a reactivity factory",id:"adding-a-reactivity-factory",level:2},{value:"Angular",id:"angular",level:3},{value:"Import",id:"import",level:4},{value:"Set the reactivity factory",id:"set-the-reactivity-factory",level:4},{value:"Use the Signal in an Angular component",id:"use-the-signal-in-an-angular-component",level:4},{value:"React",id:"react",level:3},{value:"Install Preact Signals",id:"install-preact-signals",level:4},{value:"Import",id:"import-1",level:4},{value:"Set the reactivity factory",id:"set-the-reactivity-factory-1",level:4},{value:"Use the Signal in a React component",id:"use-the-signal-in-a-react-component",level:4},{value:"Vue",id:"vue",level:3},{value:"Import",id:"import-2",level:4},{value:"Set the reactivity factory",id:"set-the-reactivity-factory-2",level:4},{value:"Use the Shallow Ref in a Vue component",id:"use-the-shallow-ref-in-a-vue-component",level:4},{value:"Accessing custom reactivity objects",id:"accessing-custom-reactivity-objects",level:2}];function p(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",p:"p",pre:"pre",span:"span",strong:"strong",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"signals--co---custom-reactivity-adapters-instead-of-rxjs-observables",children:"Signals & Co. - Custom reactivity adapters instead of RxJS Observables"})}),"\n",(0,i.jsxs)(s.p,{children:["RxDB internally uses the ",(0,i.jsx)(s.a,{href:"https://rxjs.dev/",children:"rxjs library"})," for observables and streams. All functionalities of RxDB like ",(0,i.jsx)(s.a,{href:"/rx-query.html#observe",children:"query"})," results or ",(0,i.jsx)(s.a,{href:"/rx-document.html#observe",children:"document fields"})," that expose values that change over time return a rxjs ",(0,i.jsx)(s.code,{children:"Observable"})," that allows you to observe the values and update your UI accordingly depending on the changes to the database state."]}),"\n",(0,i.jsxs)(s.p,{children:["However there are many reasons to use other reactivity libraries that use a different datatype to represent changing values. For example when you use ",(0,i.jsx)(s.strong,{children:"signals"})," in angular or react, the ",(0,i.jsx)(s.strong,{children:"template refs"})," of vue or state libraries like MobX and redux."]}),"\n",(0,i.jsxs)(s.p,{children:["RxDB allows you to pass a custom reactivity factory on ",(0,i.jsx)(s.a,{href:"/rx-database.html",children:"RxDatabase"})," creation so that you can easily access values wrapped with your custom datatype in a convenient way."]}),"\n",(0,i.jsx)(s.h2,{id:"adding-a-reactivity-factory",children:"Adding a reactivity factory"}),"\n",(0,i.jsxs)(a.t,{children:[(0,i.jsx)(s.h3,{id:"angular",children:"Angular"}),(0,i.jsxs)(s.p,{children:["In angular we use ",(0,i.jsx)(s.a,{href:"https://angular.dev/guide/signals",children:"Angular Signals"})," as custom reactivity objects."]}),(0,i.jsxs)(l.g,{children:[(0,i.jsx)(s.h4,{id:"import",children:"Import"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createReactivityFactory } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/reactivity-angular'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Injectable"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" inject } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@angular/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h4,{id:"set-the-reactivity-factory",children:"Set the reactivity factory"}),(0,i.jsxs)(s.p,{children:["Set the factory as ",(0,i.jsx)(s.code,{children:"reactivity"})," option when calling ",(0,i.jsx)(s.code,{children:"createRxDatabase"}),"."]}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" reactivity"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createReactivityFactory"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"inject"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(Injector))"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// add collections/sync etc..."})})]})})}),(0,i.jsx)(s.h4,{id:"use-the-signal-in-an-angular-component",children:"Use the Signal in an Angular component"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Component"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" inject } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@angular/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { CommonModule } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@angular/common'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { DbService } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '../db.service'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"@"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"Component"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'app-todos-list'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" standalone"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" imports"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [CommonModule]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"
"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" class"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" TodosListComponent"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" private"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" dbService "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" inject"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(DbService);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // RxDB query - Angular Signal"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" readonly"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" todosSignal "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" this"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"dbService"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "})]})})})]}),(0,i.jsxs)(s.p,{children:["An example of how signals are used in angular with RxDB, can be found at the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/blob/master/examples/angular/src/app/components/heroes-list/heroes-list.component.ts#L46",children:"RxDB Angular Example"})]}),(0,i.jsx)(s.h3,{id:"react",children:"React"}),(0,i.jsxs)(s.p,{children:["For React, we use the ",(0,i.jsx)(s.a,{href:"https://preactjs.com/guide/v10/signals/",children:"Preact Signals"})," for custom reactivity."]}),(0,i.jsxs)(l.g,{children:[(0,i.jsx)(s.h4,{id:"install-preact-signals",children:"Install Preact Signals"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" @preact/signals-core"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" --save"})]})})})}),(0,i.jsx)(s.h4,{id:"import-1",children:"Import"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" PreactSignalsRxReactivityFactory"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/reactivity-preact-signals'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h4,{id:"set-the-reactivity-factory-1",children:"Set the reactivity factory"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" reactivity"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" PreactSignalsRxReactivityFactory"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// add collections/sync etc..."})})]})})}),(0,i.jsx)(s.h4,{id:"use-the-signal-in-a-react-component",children:"Use the Signal in a React component"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"tsx","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"tsx","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { useEffect"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" useState } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'preact/hooks'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" './db'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" TodosList"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" setDb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useState"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"null"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" useEffect"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(setDb);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" []);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"db) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" null"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // RxQuery -> Preact Signal"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" todosSignal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"todosSignal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"value"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"{"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".primary}>"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".title}"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ))}"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})})]}),(0,i.jsx)(s.h3,{id:"vue",children:"Vue"}),(0,i.jsxs)(s.p,{children:["For Vue, we use the ",(0,i.jsx)(s.a,{href:"https://vuejs.org/api/reactivity-advanced",children:"Vue Shallow Refs"})," for custom reactivity."]}),(0,i.jsxs)(l.g,{children:[(0,i.jsx)(s.h4,{id:"import-2",children:"Import"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { VueRxReactivityFactory } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/reactivity-vue'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})})})}),(0,i.jsx)(s.h4,{id:"set-the-reactivity-factory-2",children:"Set the reactivity factory"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" database"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" reactivity"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" VueRxReactivityFactory"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// add collections/sync etc..."})})]})})}),(0,i.jsx)(s.h4,{id:"use-the-shallow-ref-in-a-vue-component",children:"Use the Shallow Ref in a Vue component"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"html","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"html","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"script"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" setup"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" lang"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"ts"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" './db'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// RxQuery to Vue shallowRef signal"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" todosSignal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"todos"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"script"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" v-for"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"t in todosSignal"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" :key"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"t.primary"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"label"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">{{ t.title }}"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"label"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]})]})})})]})]}),"\n",(0,i.jsx)(s.h2,{id:"accessing-custom-reactivity-objects",children:"Accessing custom reactivity objects"}),"\n",(0,i.jsxs)(s.p,{children:["All observable data in RxDB is marked by the single dollar sign ",(0,i.jsx)(s.code,{children:"$"})," like ",(0,i.jsx)(s.code,{children:"RxCollection.$"})," for events or ",(0,i.jsx)(s.code,{children:"RxDocument.myField$"})," to get the observable for a document field. To make custom reactivity objects distinguable, they are marked with double-dollar signs ",(0,i.jsx)(s.code,{children:"$$"})," instead. Here are some example on how to get custom reactivity objects from RxDB specific instances:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// RxDocument"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get signal that represents the document field 'foobar'"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get$$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// same as above"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".foobar$$;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get signal that represents whole document over time"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".$$;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get signal that represents the deleted state of the document"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".deleted$$;"})]})]})})}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// RxQuery"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get signal that represents the query result set over time"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get signal that represents the query result set over time"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"().$$;"})]})]})})}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// RxLocalDocument"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get signal that represents the whole local document state"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxLocalDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".$$;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get signal that represents the foobar field"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" signal"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxLocalDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get$$"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'foobar'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})})]})}function k(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(p,{...e})}):p(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a574e172.f6f58d32.js b/docs/assets/js/a574e172.f6f58d32.js
deleted file mode 100644
index b5548e54e2e..00000000000
--- a/docs/assets/js/a574e172.f6f58d32.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7149],{3247(e,s,n){n.d(s,{g:()=>o});var r=n(4848);function o(e){const s=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&s.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&s.push(n),(0,r.jsx)("div",{style:i.stepsContainer,children:s.map((e,s)=>(0,r.jsxs)("div",{style:i.stepWrapper,children:[(0,r.jsxs)("div",{style:i.stepIndicator,children:[(0,r.jsx)("div",{style:i.stepNumber,children:s+1}),(0,r.jsx)("div",{style:i.verticalLine})]}),(0,r.jsxs)("div",{style:i.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,r.jsx)("div",{style:i.item,children:e},s))]})]},s))})}const i={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5384(e,s,n){n.r(s),n.d(s,{assets:()=>c,contentTitle:()=>a,default:()=>p,frontMatter:()=>t,metadata:()=>r,toc:()=>h});const r=JSON.parse('{"id":"replication-http","title":"HTTP Replication","description":"Learn how to establish HTTP replication between RxDB clients and a Node.js Express server for data synchronization.","source":"@site/docs/replication-http.md","sourceDirName":".","slug":"/replication-http.html","permalink":"/replication-http.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"HTTP Replication","slug":"replication-http.html","description":"Learn how to establish HTTP replication between RxDB clients and a Node.js Express server for data synchronization."},"sidebar":"tutorialSidebar","previous":{"title":"\u2699\ufe0f Sync Engine","permalink":"/replication.html"},"next":{"title":"RxServer Replication","permalink":"/replication-server.html"}}');var o=n(4848),i=n(8453),l=n(3247);n(2636);const t={title:"HTTP Replication",slug:"replication-http.html",description:"Learn how to establish HTTP replication between RxDB clients and a Node.js Express server for data synchronization."},a="HTTP Replication from a custom server to RxDB clients",c={},h=[{value:"Setup",id:"setup",level:2},{value:"Start the Replication on the RxDB Client",id:"start-the-replication-on-the-rxdb-client",level:3},{value:"Start a Node.js process with Express and MongoDB",id:"start-a-nodejs-process-with-express-and-mongodb",level:3},{value:"Implement the Pull Endpoint",id:"implement-the-pull-endpoint",level:3},{value:"Implement the Pull Handler",id:"implement-the-pull-handler",level:3},{value:"Implement the Push Endpoint",id:"implement-the-push-endpoint",level:3},{value:"Implement the Push Handler",id:"implement-the-push-handler",level:3},{value:"Implement the pullStream$ Endpoint",id:"implement-the-pullstream-endpoint",level:3},{value:"Implement the pullStream$ Handler",id:"implement-the-pullstream-handler",level:3},{value:"pullStream$ RESYNC flag",id:"pullstream-resync-flag",level:3},{value:"Missing implementation details",id:"missing-implementation-details",level:2}];function d(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(s.header,{children:(0,o.jsx)(s.h1,{id:"http-replication-from-a-custom-server-to-rxdb-clients",children:"HTTP Replication from a custom server to RxDB clients"})}),"\n",(0,o.jsxs)(s.p,{children:["While RxDB has a range of backend-specific replication plugins (like ",(0,o.jsx)(s.a,{href:"/replication-graphql.html",children:"GraphQL"})," or ",(0,o.jsx)(s.a,{href:"/replication-firestore.html",children:"Firestore"}),"), the replication is build in a way to make it very easy to replicate data from a custom server to RxDB clients."]}),"\n",(0,o.jsx)("p",{align:"center",children:(0,o.jsx)("img",{src:"./files/icons/with-gradient/replication.svg",alt:"HTTP replication",height:"60"})}),"\n",(0,o.jsxs)(s.p,{children:["Using ",(0,o.jsx)(s.strong,{children:"HTTP"})," as a transport protocol makes it simple to create a compatible backend on top of your existing infrastructure. For events that must be sent from the server to the client, we can use ",(0,o.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events",children:"Server Send Events"}),"."]}),"\n",(0,o.jsx)(s.p,{children:"In this tutorial we will implement a HTTP replication between an RxDB client and a MongoDB express server. You can adapt this for any other backend database technology like PostgreSQL or even a non-Node.js server like go or java."}),"\n",(0,o.jsxs)(s.p,{children:["To create a compatible server for replication, we will start a server and implement the correct HTTP routes and replication handlers. We need a push-handler, a pull-handler and for the ongoing changes ",(0,o.jsx)(s.code,{children:"pull.stream"})," we use ",(0,o.jsx)(s.strong,{children:"Server Send Events"}),"."]}),"\n",(0,o.jsx)(s.h2,{id:"setup",children:"Setup"}),"\n",(0,o.jsxs)(l.g,{children:[(0,o.jsx)(s.h3,{id:"start-the-replication-on-the-rxdb-client",children:"Start the Replication on the RxDB Client"}),(0,o.jsxs)(s.p,{children:["RxDB does not have a specific HTTP-replication plugin because the ",(0,o.jsx)(s.a,{href:"/replication.html",children:"replication primitives plugin"})," is simple enough to start a HTTP replication on top of it.\nWe import the ",(0,o.jsx)(s.code,{children:"replicateRxCollection"})," function and start the replication from there for a single ",(0,o.jsx)(s.a,{href:"/rx-collection.html",children:"RxCollection"}),"."]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > client.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateRxCollection } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-http-replication'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* add settings from below */"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* add settings from below */"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.h3,{id:"start-a-nodejs-process-with-express-and-mongodb",children:"Start a Node.js process with Express and MongoDB"}),(0,o.jsx)(s.p,{children:"On the server side, we start an express server that has a MongoDB connection and serves the HTTP requests of the client."}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > server.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { MongoClient } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mongodb'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" express "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'express'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoClient"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" MongoClient"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'mongodb://localhost:27017/'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoConnection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoClient"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".connect"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoConnection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".db"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'myDatabase'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoDatabase"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".collection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'myDocs'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" app"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" express"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".use"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"express"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... add routes from below */"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".listen"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"80"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`Example app listening on port 80`"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.h3,{id:"implement-the-pull-endpoint",children:"Implement the Pull Endpoint"}),(0,o.jsxs)(s.p,{children:["As first HTTP Endpoint, we need to implement the pull handler. This is used by the RxDB replication to fetch all documents writes that happened after a given ",(0,o.jsx)(s.code,{children:"checkpoint"}),"."]}),(0,o.jsxs)(s.p,{children:["The ",(0,o.jsx)(s.code,{children:"checkpoint"})," format is not determined by RxDB, instead the server can use any type of changepoint that can be used to iterate across document writes. Here we will just use a unix timestamp ",(0,o.jsx)(s.code,{children:"updatedAt"})," and a string ",(0,o.jsx)(s.code,{children:"id"})," which is the most common used format."]}),(0,o.jsx)(s.p,{children:"When the pull endpoint is called, the server responds with an array of document data based on the given checkpoint and a new checkpoint.\nAlso the server has to respect the batchSize so that RxDB knows when there are no more new documents and the server returns a non-full array."}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > server.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { lastOfArray } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'/pull'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" res) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"query"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".id;"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" updatedAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" parseFloat"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"query"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt);"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $or"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * Notice that we have to compare the updatedAt AND the id field"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * because the updateAt field is not unique and when two documents"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:' * have the same updateAt, we can still "sort" them by their id.'})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" updateAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { $gt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt }"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" updateAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { $eq"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt }"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id: { $gt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id }"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .sort"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({updateAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"})"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .limit"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"parseInt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"query"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".batchSize"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"))"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".toArray"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" newCheckpoint"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ==="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ?"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" lastOfArray"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(documents).id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" lastOfArray"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(documents).updatedAt"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".setHeader"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Content-Type'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".end"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" newCheckpoint }));"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.h3,{id:"implement-the-pull-handler",children:"Implement the Pull Handler"}),(0,o.jsxs)(s.p,{children:["On the client we add the ",(0,o.jsx)(s.code,{children:"pull.handler"})," to the replication setting. The handler request the correct server url and fetches the documents."]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > client.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(checkpointOrNull"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize){"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" updatedAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" checkpointOrNull "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"?"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" checkpointOrNull"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" checkpointOrNull "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"?"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" checkpointOrNull"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".id "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ''"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `https://localhost/pull?updatedAt="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"updatedAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"&id="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"&limit="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"${"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"batchSize"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"}"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"`"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" data"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" data"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" data"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".checkpoint"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.h3,{id:"implement-the-push-endpoint",children:"Implement the Push Endpoint"}),(0,o.jsxs)(s.p,{children:["To send client side writes to the server, we have to implement the ",(0,o.jsx)(s.code,{children:"push.handler"}),". It gets an array of change rows as input and has to return only the conflicting documents that did not have been written to the server. Each change row contains a ",(0,o.jsx)(s.code,{children:"newDocumentState"})," and an optional ",(0,o.jsx)(s.code,{children:"assumedMasterState"}),"."]}),(0,o.jsxs)(s.p,{children:["For ",(0,o.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"conflict detection"}),", on the server we first have to detect if the ",(0,o.jsx)(s.code,{children:"assumedMasterState"}),' is correct for each row. If yes, we have to write the new document state to the database, otherwise we have to return the "real" master state in the conflict array.']}),(0,o.jsxs)(s.p,{children:["The server also creates an ",(0,o.jsx)(s.code,{children:"event"})," that is emitted to the ",(0,o.jsx)(s.code,{children:"pullStream$"})," which is later used in the ",(0,o.jsx)(s.a,{href:"#pullstream-for-ongoing-changes",children:"pull.stream$"}),"."]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > server.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { lastOfArray } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Subject } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxjs'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// used in the pull.stream$ below"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastEventId "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Subject"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'/push'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" res) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRows"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".body;"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" conflicts"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [];"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" event"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastEventId"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"++"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" []"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" null"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" for"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" of"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" changeRows){"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" realMasterState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".findOne"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"newDocumentState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".id});"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" realMasterState "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&&"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" !"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".assumedMasterState "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"||"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" realMasterState "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&&"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".assumedMasterState "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"&&"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /*"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * For simplicity we detect conflicts on the server by only compare the updateAt value."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" * In reality you might want to do a more complex check or do a deep-equal comparison."})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" realMasterState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!=="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"assumedMasterState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" )"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ) {"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // we have a conflict"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" conflicts"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".push"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(realMasterState);"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"else"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // no conflict -> write the document"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mongoCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".updateOne"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"newDocumentState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".id}"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".newDocumentState"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" event"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".push"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".newDocumentState);"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" event"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".checkpoint "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"newDocumentState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".id"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" changeRow"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"newDocumentState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt };"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"event"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" >"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"){"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".next"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(event);"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".setHeader"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Content-Type'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".end"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"JSON"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(conflicts));"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.admonition,{type:"note",children:(0,o.jsx)(s.p,{children:"For simplicity in this tutorial, we do not use transactions. In reality you should run the full push function inside of a MongoDB transaction to ensure that no other process can mix up the document state while the writes are processed. Also you should call batch operations on MongoDB instead of running the operations for each change row."})}),(0,o.jsx)(s.h3,{id:"implement-the-push-handler",children:"Implement the Push Handler"}),(0,o.jsxs)(s.p,{children:["With the push endpoint in place, we can add a ",(0,o.jsx)(s.code,{children:"push.handler"})," to the replication settings on the client."]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > client.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(changeRows){"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" rawResponse"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'https://localhost/push'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" method"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'POST'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Accept'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Content-Type'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'application/json'"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" body"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(changeRows)"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" conflictsArray"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" rawResponse"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" conflictsArray;"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.h3,{id:"implement-the-pullstream-endpoint",children:"Implement the pullStream$ Endpoint"}),(0,o.jsxs)(s.p,{children:["While the normal pull handler is used when the replication is in ",(0,o.jsx)(s.a,{href:"/replication.html#checkpoint-iteration",children:"iteration mode"}),", we also need a stream of ongoing changes when the replication is in ",(0,o.jsx)(s.a,{href:"/replication.html#event-observation",children:"event observation mode"}),". This brings the realtime replication to RxDB where changes on the server or on a client will directly get propagated to the other instances."]}),(0,o.jsxs)(s.p,{children:["On the server we have to implement the ",(0,o.jsx)(s.code,{children:"pullStream"})," route and emit the events. We use the ",(0,o.jsx)(s.code,{children:"pullStream$"})," observable from ",(0,o.jsx)(s.a,{href:"#push-from-the-client-to-the-server",children:"above"})," to fetch all ongoing events and respond them to the client. Here we use Server-Sent-Events (SSE) which is the most common used way to stream data from the server to the client. Other method also exist like ",(0,o.jsx)(s.a,{href:"/articles/websockets-sse-polling-webrtc-webtransport.html",children:"WebSockets or Long-Polling"}),"."]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > server.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"app"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".get"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'/pullStream'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" res) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".writeHead"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"200"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Content-Type'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/event-stream'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Connection'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'keep-alive'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Cache-Control'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'no-cache'"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" subscription"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(event "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" res"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".write"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'data: '"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(event) "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '\\n\\n'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" req"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".on"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'close'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" subscription"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".unsubscribe"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"());"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.admonition,{type:"note",children:(0,o.jsxs)(s.p,{children:["How the build the ",(0,o.jsx)(s.code,{children:"pullStream$"})," Observable is not part of this tutorial. This heavily depends on your backend and infrastructure. Likely you have to observe the MongoDB event stream."]})}),(0,o.jsx)(s.h3,{id:"implement-the-pullstream-handler",children:"Implement the pullStream$ Handler"}),(0,o.jsxs)(s.p,{children:["From the client we can observe this endpoint and create a ",(0,o.jsx)(s.code,{children:"pull.stream$"})," observable that emits all events that are send from the server to the client.\nThe client connects to an url and receives server-sent-events that contain all ongoing writes."]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > client.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Subject } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxjs'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Subject"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" eventSource"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" EventSource"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://localhost/pullStream'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { withCredentials"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"eventSource"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onmessage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" event "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" eventData"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".parse"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"event"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".data);"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".next"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" eventData"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".documents"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" eventData"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".checkpoint"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" stream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".asObservable"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),(0,o.jsx)(s.h3,{id:"pullstream-resync-flag",children:"pullStream$ RESYNC flag"}),(0,o.jsxs)(s.p,{children:["In case the client looses the connection, the EventSource will automatically reconnect but there might have been some changes that have been missed out in the meantime. The replication has to be informed that it might have missed events by emitting a ",(0,o.jsx)(s.code,{children:"RESYNC"})," flag from the ",(0,o.jsx)(s.code,{children:"pull.stream$"}),".\nThe replication will then catch up by switching to the ",(0,o.jsx)(s.a,{href:"/replication.html#checkpoint-iteration",children:"iteration mode"})," until it is in sync with the server again."]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > client.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"eventSource"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onerror"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".next"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'RESYNC'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),(0,o.jsxs)(s.p,{children:["The purpose of the ",(0,o.jsx)(s.code,{children:"RESYNC"}),' flag is to tell the client that "something might have changed" and then the client can react on that information without having to run operations in an interval.']}),(0,o.jsxs)(s.p,{children:["If your backend is not capable of emitting the actual documents and checkpoint in the pull stream, you could just map all events to the ",(0,o.jsx)(s.code,{children:"RESYNC"})," flag. This would make the replication work with a slight performance drawback:"]}),(0,o.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,o.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,o.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// > client.ts"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { Subject } "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxjs'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Subject"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" eventSource"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" EventSource"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://localhost/pullStream'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { withCredentials"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"eventSource"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onmessage"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" () "}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".next"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'RESYNC'"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,o.jsxs)(s.span,{"data-line":"",children:[(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" stream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myPullStream$"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".asObservable"}),(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,o.jsx)(s.span,{"data-line":"",children:(0,o.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})})]}),"\n",(0,o.jsx)(s.h2,{id:"missing-implementation-details",children:"Missing implementation details"}),"\n",(0,o.jsx)(s.p,{children:"In this tutorial we only covered the basics of doing a HTTP replication between RxDB clients and a server. We did not cover the following aspects of the implementation:"}),"\n",(0,o.jsxs)(s.ul,{children:["\n",(0,o.jsx)(s.li,{children:"Authentication: To authenticate the client on the server, you might want to send authentication headers with the HTTP requests"}),"\n",(0,o.jsxs)(s.li,{children:["Skip events on the ",(0,o.jsx)(s.code,{children:"pull.stream$"})," for the client that caused the changes to improve performance."]}),"\n",(0,o.jsxs)(s.li,{children:["Version upgrades: You should add a version-flag to the endpoint urls. If you then update the version of your endpoints in any way, your old endpoints should emit a ",(0,o.jsx)(s.code,{children:"Code 426"})," to outdated clients so that they can updated their client version."]}),"\n"]})]})}function p(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,o.jsx)(s,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>l,x:()=>t});var r=n(6540);const o={},i=r.createContext(o);function l(e){const s=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:l(e.components),r.createElement(i.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a69eebfc.8659aa3b.js b/docs/assets/js/a69eebfc.8659aa3b.js
deleted file mode 100644
index b419f37e88e..00000000000
--- a/docs/assets/js/a69eebfc.8659aa3b.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9408],{5878(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>t,metadata:()=>r,toc:()=>d});const r=JSON.parse('{"id":"query-optimizer","title":"Optimize Client-Side Queries with RxDB","description":"Harness real-world data to fine-tune queries. The build-time RxDB Optimizer finds the perfect index, boosting query speed in any environment.","source":"@site/docs/query-optimizer.md","sourceDirName":".","slug":"/query-optimizer.html","permalink":"/query-optimizer.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Optimize Client-Side Queries with RxDB","slug":"query-optimizer.html","description":"Harness real-world data to fine-tune queries. The build-time RxDB Optimizer finds the perfect index, boosting query speed in any environment."},"sidebar":"tutorialSidebar","previous":{"title":"Vector Database","permalink":"/articles/javascript-vector-database.html"},"next":{"title":"Third Party Plugins","permalink":"/third-party-plugins.html"}}');var i=s(4848),o=s(8453);const t={title:"Optimize Client-Side Queries with RxDB",slug:"query-optimizer.html",description:"Harness real-world data to fine-tune queries. The build-time RxDB Optimizer finds the perfect index, boosting query speed in any environment."},a="Query Optimizer",l={},d=[{value:"Usage",id:"usage",level:2},{value:"Important details",id:"important-details",level:2}];function c(e){const n={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"query-optimizer",children:"Query Optimizer"})}),"\n",(0,i.jsx)(n.p,{children:"The query optimizer can be used to determine which index is the best to use for a given query.\nBecause RxDB is used in client side applications, it cannot do any background checks or measurements to optimize the query plan because that would cause significant performance problems."}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["The query optimizer is part of the ",(0,i.jsx)(n.a,{href:"/premium/",children:"RxDB Premium \ud83d\udc51"})," plugin that must be purchased. It is not part of the default RxDB module."]})}),"\n",(0,i.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" findBestIndex"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/query-optimizer'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { "})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageIndexedDB"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/indexeddb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" bestIndexes"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" findBestIndex"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxJsonSchema"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * In this example we use the IndexedDB RxStorage,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * but any other storage can be used for testing."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Multiple queries can be optimized at the same time"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * which decreases the overall runtime."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" queries"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Queries can be mapped by a query id,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * here we use myFirstQuery as query id."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myFirstQuery"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" mySecondQuery"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" age"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" $eq"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Nakamoto'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" testData"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/** data for the documents. **/"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsx)(n.h2,{id:"important-details",children:"Important details"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["This is a build time tool. You should use it to find the best indexes for your queries during ",(0,i.jsx)(n.strong,{children:"build time"}),". Then you store these results and you application can use the best indexes during ",(0,i.jsx)(n.strong,{children:"run time"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["It makes no sense to run time optimization with a different ",(0,i.jsx)(n.code,{children:"RxStorage"})," (+settings) that what you use in production. The result of the query optimizer is heavily dependent on the RxStorage and JavaScript runtime. For example it makes no sense to run the optimization in Node.js and then use the optimized indexes in the browser."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["It is very important that you use ",(0,i.jsx)(n.strong,{children:"production like"})," ",(0,i.jsx)(n.code,{children:"testData"}),". Finding the best index heavily depends on data distribution and amount of stored/queried documents. For example if you store and query users with an ",(0,i.jsx)(n.code,{children:"age"})," field, it makes no sense to just use a random number for the age because in production the ",(0,i.jsx)(n.code,{children:"age"})," of your users is not equally distributed."]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:["The higher you set ",(0,i.jsx)(n.code,{children:"runs"}),", the more test cycles will be performed and the more ",(0,i.jsx)(n.strong,{children:"significant"})," will be the time measurements which leads to a better index selection."]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(c,{...e})}):c(e)}},8453(e,n,s){s.d(n,{R:()=>t,x:()=>a});var r=s(6540);const i={},o=r.createContext(i);function t(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:t(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a7456010.4971822d.js b/docs/assets/js/a7456010.4971822d.js
deleted file mode 100644
index cb5f7980354..00000000000
--- a/docs/assets/js/a7456010.4971822d.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1235],{8552(s){s.exports=JSON.parse('{"name":"docusaurus-plugin-content-pages","id":"default"}')}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a7bd4aaa.992c4a90.js b/docs/assets/js/a7bd4aaa.992c4a90.js
deleted file mode 100644
index e2ca5cf27f4..00000000000
--- a/docs/assets/js/a7bd4aaa.992c4a90.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[7098],{1723(n,e,s){s.r(e),s.d(e,{default:()=>d});s(6540);var r=s(5500);function o(n,e){return`docs-${n}-${e}`}var t=s(3025),i=s(2831),c=s(1463),a=s(4848);function u(n){const{version:e}=n;return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(c.A,{version:e.version,tag:o(e.pluginId,e.version)}),(0,a.jsx)(r.be,{children:e.noIndex&&(0,a.jsx)("meta",{name:"robots",content:"noindex, nofollow"})})]})}function l(n){const{version:e,route:s}=n;return(0,a.jsx)(r.e3,{className:e.className,children:(0,a.jsx)(t.n,{version:e,children:(0,i.v)(s.routes)})})}function d(n){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(u,{...n}),(0,a.jsx)(l,{...n})]})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a7f10198.cfa2de30.js b/docs/assets/js/a7f10198.cfa2de30.js
deleted file mode 100644
index 98ef90b09f5..00000000000
--- a/docs/assets/js/a7f10198.cfa2de30.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1098],{1337(t,e,n){n.r(e),n.d(e,{assets:()=>c,contentTitle:()=>s,default:()=>d,frontMatter:()=>i,metadata:()=>a,toc:()=>m});const a=JSON.parse('{"id":"data-migration","title":"Data Migration","description":"This documentation page has been moved to here","source":"@site/docs/data-migration.md","sourceDirName":".","slug":"/data-migration.html","permalink":"/data-migration.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Data Migration","slug":"data-migration.html"}}');var o=n(4848),r=n(8453);const i={title:"Data Migration",slug:"data-migration.html"},s=void 0,c={},m=[];function l(t){const e={a:"a",p:"p",...(0,r.R)(),...t.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)(e.p,{children:["This documentation page has been moved to ",(0,o.jsx)(e.a,{href:"/migration-schema.html",children:"here"})]}),"\n",(0,o.jsx)("meta",{"http-equiv":"refresh",content:"0; url=https://rxdb.info/migration-schema.html"})]})}function d(t={}){const{wrapper:e}={...(0,r.R)(),...t.components};return e?(0,o.jsx)(e,{...t,children:(0,o.jsx)(l,{...t})}):l(t)}},8453(t,e,n){n.d(e,{R:()=>i,x:()=>s});var a=n(6540);const o={},r=a.createContext(o);function i(t){const e=a.useContext(r);return a.useMemo(function(){return"function"==typeof t?t(e):{...e,...t}},[e,t])}function s(t){let e;return e=t.disableParentContext?"function"==typeof t.components?t.components(o):t.components||o:i(t.components),a.createElement(r.Provider,{value:e},t.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/a94703ab.13c96c74.js b/docs/assets/js/a94703ab.13c96c74.js
deleted file mode 100644
index cb83fcdf138..00000000000
--- a/docs/assets/js/a94703ab.13c96c74.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9048],{5955(e,t,n){n.d(t,{A:()=>r});n(6540);var a=n(4164),i=n(1312),s=n(1107),o=n(4848);function r({className:e}){return(0,o.jsx)("main",{className:(0,a.A)("container margin-vert--xl",e),children:(0,o.jsx)("div",{className:"row",children:(0,o.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,o.jsxs)(s.A,{as:"h1",className:"hero__title",children:[(0,o.jsx)("a",{href:"/",children:(0,o.jsx)("div",{style:{textAlign:"center"},children:(0,o.jsx)("img",{src:"https://rxdb.info/files/logo/rxdb_javascript_database.svg",alt:"RxDB",width:"160"})})}),(0,o.jsx)(i.A,{id:"theme.NotFound.title",description:"The title of the 404 page",children:"404 Page Not Found"})]}),(0,o.jsx)("p",{children:(0,o.jsx)(i.A,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page",children:"The page you are looking for does not exist anymore or never has existed. If you have found this page through a link, you should tell the link author to update it."})}),(0,o.jsx)("p",{children:"Maybe one of these can help you to find the desired content:"}),(0,o.jsx)("div",{className:"ul-container",children:(0,o.jsxs)("ul",{children:[(0,o.jsx)("li",{children:(0,o.jsx)("a",{href:"https://rxdb.info/overview.html",children:"RxDB Documentation"})}),(0,o.jsx)("li",{children:(0,o.jsx)("a",{href:"/chat/",children:"RxDB Discord Channel"})}),(0,o.jsx)("li",{children:(0,o.jsx)("a",{href:"https://twitter.com/intent/user?screen_name=rxdbjs",children:"RxDB on twitter"})}),(0,o.jsx)("li",{children:(0,o.jsx)("a",{href:"/code/",children:"RxDB at Github"})})]})})]})})})}},7510(e,t,n){n.r(t),n.d(t,{default:()=>Ne});var a=n(6540),i=n(4164),s=n(5500),o=n(7559),r=n(4718),l=n(609),c=n(1312),d=n(3104),u=n(5062);const h="backToTopButton_sjWU",m="backToTopButtonShow_xfvO";var b=n(4848);function p(){const{shown:e,scrollToTop:t}=function({threshold:e}){const[t,n]=(0,a.useState)(!1),i=(0,a.useRef)(!1),{startScroll:s,cancelScroll:o}=(0,d.gk)();return(0,d.Mq)(({scrollY:t},a)=>{const s=a?.scrollY;s&&(i.current?i.current=!1:t>=s?(o(),n(!1)):t{e.location.hash&&(i.current=!0,n(!1))}),{shown:t,scrollToTop:()=>s(0)}}({threshold:300});return(0,b.jsx)("button",{"aria-label":(0,c.T)({id:"theme.BackToTopButton.buttonAriaLabel",message:"Scroll back to top",description:"The ARIA label for the back to top button"}),className:(0,i.A)("clean-btn",o.G.common.backToTopButton,h,e&&m),type:"button",onClick:t})}var x=n(3109),j=n(6347),f=n(4581),g=n(6342),v=n(9529);function _(e){return(0,b.jsx)("svg",{width:"20",height:"20","aria-hidden":"true",...e,children:(0,b.jsxs)("g",{fill:"#7a7a7a",children:[(0,b.jsx)("path",{d:"M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"}),(0,b.jsx)("path",{d:"M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"})]})})}const A="collapseSidebarButton_JQG6",C="collapseSidebarButtonIcon_Iseg";function k({onClick:e}){return(0,b.jsx)("button",{type:"button",title:(0,c.T)({id:"theme.docs.sidebar.collapseButtonTitle",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),"aria-label":(0,c.T)({id:"theme.docs.sidebar.collapseButtonAriaLabel",message:"Collapse sidebar",description:"The title attribute for collapse button of doc sidebar"}),className:(0,i.A)("button button--secondary button--outline",A),onClick:e,children:(0,b.jsx)(_,{className:C})})}var S=n(5041),N=n(9532);const y=Symbol("EmptyContext"),T=a.createContext(y);function I({children:e}){const[t,n]=(0,a.useState)(null),i=(0,a.useMemo)(()=>({expandedItem:t,setExpandedItem:n}),[t]);return(0,b.jsx)(T.Provider,{value:i,children:e})}var L=n(1422),B=n(9169),w=n(8774),E=n(2303),D=n(6654),M=n(3186);const H="menuExternalLink_NmtK",R="linkLabel_WmDU";function P({label:e}){return(0,b.jsx)("span",{title:e,className:R,children:e})}function G({item:e,onItemClick:t,activePath:n,level:a,index:s,...l}){const{href:c,label:d,className:u,autoAddBaseUrl:h}=e,m=(0,r.w8)(e,n),p=(0,D.A)(c);return(0,b.jsx)("li",{className:(0,i.A)(o.G.docs.docSidebarItemLink,o.G.docs.docSidebarItemLinkLevel(a),"menu__list-item",u),children:(0,b.jsxs)(w.A,{className:(0,i.A)("menu__link",!p&&H,{"menu__link--active":m}),autoAddBaseUrl:h,"aria-current":m?"page":void 0,to:c,...p&&{onClick:t?()=>t(e):void 0},...l,children:[(0,b.jsx)(P,{label:d}),!p&&(0,b.jsx)(M.A,{})]})},d)}const W="categoryLink_byQd",U="categoryLinkLabel_W154";function Y({collapsed:e,categoryLabel:t,onClick:n}){return(0,b.jsx)("button",{"aria-label":e?(0,c.T)({id:"theme.DocSidebarItem.expandCategoryAriaLabel",message:"Expand sidebar category '{label}'",description:"The ARIA label to expand the sidebar category"},{label:t}):(0,c.T)({id:"theme.DocSidebarItem.collapseCategoryAriaLabel",message:"Collapse sidebar category '{label}'",description:"The ARIA label to collapse the sidebar category"},{label:t}),"aria-expanded":!e,type:"button",className:"clean-btn menu__caret",onClick:n})}function F({label:e}){return(0,b.jsx)("span",{title:e,className:U,children:e})}function V(e){return 0===(0,r.Y)(e.item.items,e.activePath).length?(0,b.jsx)(z,{...e}):(0,b.jsx)(K,{...e})}function z({item:e,...t}){if("string"!=typeof e.href)return null;const{type:n,collapsed:a,collapsible:i,items:s,linkUnlisted:o,...r}=e,l={type:"link",...r};return(0,b.jsx)(G,{item:l,...t})}function K({item:e,onItemClick:t,activePath:n,level:s,index:l,...c}){const{items:d,label:u,collapsible:h,className:m,href:p}=e,{docs:{sidebar:{autoCollapseCategories:x}}}=(0,g.p)(),j=function(e){const t=(0,E.A)();return(0,a.useMemo)(()=>e.href&&!e.linkUnlisted?e.href:!t&&e.collapsible?(0,r.Nr)(e):void 0,[e,t])}(e),f=(0,r.w8)(e,n),v=(0,B.ys)(p,n),{collapsed:_,setCollapsed:A}=(0,L.u)({initialState:()=>!!h&&(!f&&e.collapsed)}),{expandedItem:C,setExpandedItem:k}=function(){const e=(0,a.useContext)(T);if(e===y)throw new N.dV("DocSidebarItemsExpandedStateProvider");return e}(),S=(e=!_)=>{k(e?null:l),A(e)};!function({isActive:e,collapsed:t,updateCollapsed:n,activePath:i}){const s=(0,N.ZC)(e),o=(0,N.ZC)(i);(0,a.useEffect)(()=>{(e&&!s||e&&s&&i!==o)&&t&&n(!1)},[e,s,t,n,i,o])}({isActive:f,collapsed:_,updateCollapsed:S,activePath:n}),(0,a.useEffect)(()=>{h&&null!=C&&C!==l&&x&&A(!0)},[h,C,l,A,x]);return(0,b.jsxs)("li",{className:(0,i.A)(o.G.docs.docSidebarItemCategory,o.G.docs.docSidebarItemCategoryLevel(s),"menu__list-item",{"menu__list-item--collapsed":_},m),children:[(0,b.jsxs)("div",{className:(0,i.A)("menu__list-item-collapsible",{"menu__list-item-collapsible--active":v}),children:[(0,b.jsx)(w.A,{className:(0,i.A)(W,"menu__link",{"menu__link--sublist":h,"menu__link--sublist-caret":!p&&h,"menu__link--active":f}),onClick:n=>{t?.(e),h&&(p?v?(n.preventDefault(),S()):S(!1):(n.preventDefault(),S()))},"aria-current":v?"page":void 0,role:h&&!p?"button":void 0,"aria-expanded":h&&!p?!_:void 0,href:h?j??"#":j,...c,children:(0,b.jsx)(F,{label:u})}),p&&h&&(0,b.jsx)(Y,{collapsed:_,categoryLabel:u,onClick:e=>{e.preventDefault(),S()}})]}),(0,b.jsx)(L.N,{lazy:!0,as:"ul",className:"menu__list",collapsed:_,children:(0,b.jsx)(J,{items:d,tabIndex:_?-1:0,onItemClick:t,activePath:n,level:s+1})})]})}const O="menuHtmlItem_M9Kj";function Q({item:e,level:t,index:n}){const{value:a,defaultStyle:s,className:r}=e;return(0,b.jsx)("li",{className:(0,i.A)(o.G.docs.docSidebarItemLink,o.G.docs.docSidebarItemLinkLevel(t),s&&[O,"menu__list-item"],r),dangerouslySetInnerHTML:{__html:a}},n)}function Z({item:e,...t}){switch(e.type){case"category":return(0,b.jsx)(V,{item:e,...t});case"html":return(0,b.jsx)(Q,{item:e,...t});default:return(0,b.jsx)(G,{item:e,...t})}}function q({items:e,...t}){const n=(0,r.Y)(e,t.activePath);return(0,b.jsx)(I,{children:n.map((e,n)=>(0,b.jsx)(Z,{item:e,index:n,...t},n))})}const J=(0,a.memo)(q),X="menu_Y1UP",$="menuWithAnnouncementBar_fPny";var ee=n(8120);function te({path:e,sidebar:t,className:n}){const s=function(){const{isActive:e}=(0,S.M)(),[t,n]=(0,a.useState)(e);return(0,d.Mq)(({scrollY:t})=>{e&&n(0===t)},[e]),e&&t}();return(0,b.jsxs)("nav",{"aria-label":(0,c.T)({id:"theme.docs.sidebar.navAriaLabel",message:"Docs sidebar",description:"The ARIA label for the sidebar navigation"}),className:(0,i.A)("menu thin-scrollbar",X,s&&$,n),children:[(0,b.jsx)("div",{style:{padding:10,paddingTop:30,marginLeft:"auto",marginRight:"auto"},children:(0,b.jsx)(ee.A,{})}),(0,b.jsx)("ul",{className:(0,i.A)(o.G.docs.docSidebarMenu,"menu__list"),children:(0,b.jsx)(J,{items:t,activePath:e,level:1})})]})}const ne="sidebar_mhZE",ae="sidebarWithHideableNavbar__6UL",ie="sidebarHidden__LRd",se="sidebarLogo_F_0z";function oe({path:e,sidebar:t,onCollapse:n,isHidden:a}){const{navbar:{hideOnScroll:s},docs:{sidebar:{hideable:o}}}=(0,g.p)();return(0,b.jsxs)("div",{className:(0,i.A)(ne,s&&ae,a&&ie),children:[s&&(0,b.jsx)(v.A,{tabIndex:-1,className:se}),(0,b.jsx)(te,{path:e,sidebar:t}),o&&(0,b.jsx)(k,{onClick:n})]})}const re=a.memo(oe);var le=n(5600),ce=n(2069);const de=({sidebar:e,path:t})=>{const n=(0,ce.M)();return(0,b.jsxs)("ul",{className:(0,i.A)(o.G.docs.docSidebarMenu,"menu__list"),children:[(0,b.jsx)(ee.A,{}),(0,b.jsx)(J,{items:e,activePath:t,onItemClick:e=>{"category"===e.type&&e.href&&n.toggle(),"link"===e.type&&n.toggle()},level:1})]})};function ue(e){return(0,b.jsx)(le.GX,{component:de,props:e})}const he=a.memo(ue);function me(e){const t=(0,f.l)(),n="desktop"===t||"ssr"===t,a="mobile"===t;return(0,b.jsxs)(b.Fragment,{children:[n&&(0,b.jsx)(re,{...e}),a&&(0,b.jsx)(he,{...e})]})}const be="expandButton_TmdG",pe="expandButtonIcon_i1dp";function xe({toggleSidebar:e}){return(0,b.jsx)("div",{className:be,title:(0,c.T)({id:"theme.docs.sidebar.expandButtonTitle",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),"aria-label":(0,c.T)({id:"theme.docs.sidebar.expandButtonAriaLabel",message:"Expand sidebar",description:"The ARIA label and title attribute for expand button of doc sidebar"}),tabIndex:0,role:"button",onKeyDown:e,onClick:e,children:(0,b.jsx)(_,{className:pe})})}const je={docSidebarContainer:"docSidebarContainer_YfHR",docSidebarContainerHidden:"docSidebarContainerHidden_DPk8",sidebarViewport:"sidebarViewport_aRkj"};function fe({children:e}){const t=(0,l.t)();return(0,b.jsx)(a.Fragment,{children:e},t?.name??"noSidebar")}function ge({sidebar:e,hiddenSidebarContainer:t,setHiddenSidebarContainer:n}){const{pathname:s}=(0,j.zy)(),[r,l]=(0,a.useState)(!1),c=(0,a.useCallback)(()=>{r&&l(!1),!r&&(0,x.O)()&&l(!0),n(e=>!e)},[n,r]);return(0,b.jsx)("aside",{className:(0,i.A)(o.G.docs.docSidebarContainer,je.docSidebarContainer,t&&je.docSidebarContainerHidden),onTransitionEnd:e=>{e.currentTarget.classList.contains(je.docSidebarContainer)&&t&&l(!0)},children:(0,b.jsx)(fe,{children:(0,b.jsxs)("div",{className:(0,i.A)(je.sidebarViewport,r&&je.sidebarViewportHidden),children:[(0,b.jsx)(me,{sidebar:e,path:s,onCollapse:c,isHidden:r}),r&&(0,b.jsx)(xe,{toggleSidebar:c})]})})})}const ve={docMainContainer:"docMainContainer_TBSr",docMainContainerEnhanced:"docMainContainerEnhanced_lQrH",docItemWrapperEnhanced:"docItemWrapperEnhanced_JWYK"};function _e({hiddenSidebarContainer:e,children:t}){const n=(0,l.t)();return(0,b.jsx)("main",{className:(0,i.A)(ve.docMainContainer,(e||!n)&&ve.docMainContainerEnhanced),children:(0,b.jsx)("div",{className:(0,i.A)("container padding-top--md padding-bottom--lg",ve.docItemWrapper,e&&ve.docItemWrapperEnhanced),children:t})})}const Ae="docRoot_UBD9",Ce="docsWrapper_hBAB";function ke({children:e}){const t=(0,l.t)(),[n,i]=(0,a.useState)(!1);return(0,b.jsxs)("div",{className:Ce,children:[(0,b.jsx)(p,{}),(0,b.jsxs)("div",{className:Ae,children:[t&&(0,b.jsx)(ge,{sidebar:t.items,hiddenSidebarContainer:n,setHiddenSidebarContainer:i}),(0,b.jsx)(_e,{hiddenSidebarContainer:n,children:e})]})]})}var Se=n(5955);function Ne(e){const t=(0,r.B5)(e);if(!t)return(0,b.jsx)(Se.A,{});const{docElement:n,sidebarName:a,sidebarItems:c}=t;return(0,b.jsx)(s.e3,{className:(0,i.A)(o.G.page.docsDocPage),children:(0,b.jsx)(l.V,{name:a,items:c,children:(0,b.jsx)(ke,{children:n})})})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/aa14e6b1.c1e700b0.js b/docs/assets/js/aa14e6b1.c1e700b0.js
deleted file mode 100644
index ced6ee7cc2d..00000000000
--- a/docs/assets/js/aa14e6b1.c1e700b0.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9824],{717(s,n,e){e.r(n),e.d(n,{assets:()=>t,contentTitle:()=>a,default:()=>h,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"replication-graphql","title":"GraphQL Replication","description":"The GraphQL replication provides handlers for GraphQL to run replication with GraphQL as the transportation layer.","source":"@site/docs/replication-graphql.md","sourceDirName":".","slug":"/replication-graphql.html","permalink":"/replication-graphql.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"GraphQL Replication","slug":"replication-graphql.html"},"sidebar":"tutorialSidebar","previous":{"title":"RxServer Replication","permalink":"/replication-server.html"},"next":{"title":"WebSocket Replication","permalink":"/replication-websocket.html"}}');var i=e(4848),o=e(8453);const l={title:"GraphQL Replication",slug:"replication-graphql.html"},a="Replication with GraphQL",t={},c=[{value:"Usage",id:"usage",level:2},{value:"Creating a compatible GraphQL Server",id:"creating-a-compatible-graphql-server",level:3},{value:"RxDB Client",id:"rxdb-client",level:3},{value:"Pull replication",id:"pull-replication",level:4},{value:"Push replication",id:"push-replication",level:4},{value:"Pull Stream",id:"pull-stream",level:4},{value:"Transforming null to undefined in optional fields",id:"transforming-null-to-undefined-in-optional-fields",level:3},{value:"pull.responseModifier",id:"pullresponsemodifier",level:3},{value:"push.responseModifier",id:"pushresponsemodifier",level:3},{value:"Helper Functions",id:"helper-functions",level:4},{value:"RxGraphQLReplicationState",id:"rxgraphqlreplicationstate",level:3},{value:".setHeaders()",id:"setheaders",level:4},{value:"Sending Cookies",id:"sending-cookies",level:4}];function d(s){const n={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",p:"p",pre:"pre",span:"span",strong:"strong",...(0,o.R)(),...s.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"replication-with-graphql",children:"Replication with GraphQL"})}),"\n",(0,i.jsxs)(n.p,{children:["The GraphQL replication provides handlers for GraphQL to run ",(0,i.jsx)(n.a,{href:"/replication.html",children:"replication"})," with GraphQL as the transportation layer."]}),"\n",(0,i.jsxs)(n.p,{children:["The GraphQL replication is mostly used when you already have a backend that exposes a GraphQL API that can be adjusted to serve as a replication endpoint. If you do not already have a GraphQL endpoint, using the ",(0,i.jsx)(n.a,{href:"/replication-http.html",children:"HTTP replication"})," is an easier solution."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["To play around, check out the full example of the RxDB ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/graphql",children:"GraphQL replication with server and client"})]})}),"\n",(0,i.jsx)(n.h2,{id:"usage",children:"Usage"}),"\n",(0,i.jsxs)(n.p,{children:["Before you use the GraphQL replication, make sure you've learned how the ",(0,i.jsx)(n.a,{href:"/replication.html",children:"RxDB replication"})," works."]}),"\n",(0,i.jsx)(n.h3,{id:"creating-a-compatible-graphql-server",children:"Creating a compatible GraphQL Server"}),"\n",(0,i.jsxs)(n.p,{children:["At the server-side, there must exist an endpoint which returns newer rows when the last ",(0,i.jsx)(n.code,{children:"checkpoint"})," is used as input. For example lets say you create a ",(0,i.jsx)(n.code,{children:"Query"})," ",(0,i.jsx)(n.code,{children:"pullHuman"})," which returns a list of document writes that happened after the given checkpoint."]}),"\n",(0,i.jsxs)(n.p,{children:["For the push-replication, you also need a ",(0,i.jsx)(n.code,{children:"Mutation"})," ",(0,i.jsx)(n.code,{children:"pushHuman"})," which lets RxDB update data of documents by sending the previous document state and the new client document state.\nAlso for being able to stream all ongoing events, we need a ",(0,i.jsx)(n.code,{children:"Subscription"})," called ",(0,i.jsx)(n.code,{children:"streamHuman"}),"."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"graphql","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"graphql","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"input"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" HumanInput"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"ID"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"String"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastName: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"String"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Float"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" deleted: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Boolean"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Human"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"ID"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"String"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" lastName: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"String"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Float"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" deleted: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Boolean"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"input"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"String"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Float"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" HumanPullBulk"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" documents: ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Human"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Checkpoint"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pullHuman(checkpoint: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:", limit: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Int"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"): "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"HumanPullBulk"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"input"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" HumanInputPushRow"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" assumedMasterState: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"HeroInputPushRowT0AssumedMasterStateT0"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" newDocumentState: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"HeroInputPushRowT0NewDocumentStateT0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Mutation"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" # Returns a list of all conflicts"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" # If no document write caused a conflict, return an empty list."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pushHuman(rows: ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"HumanInputPushRow"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]): ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Human"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"# headers are used to authenticate the subscriptions"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"# over websockets."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"input"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" AUTH_TOKEN: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"String"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Subscription"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" streamHuman(headers: "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"): "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"HumanPullBulk"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "})]})})}),"\n",(0,i.jsxs)(n.p,{children:["The GraphQL resolver for the ",(0,i.jsx)(n.code,{children:"pullHuman"})," would then look like:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" rootValue"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" pullHuman"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" args "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" minId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" args"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".checkpoint "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"?"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" args"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ''"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" minUpdatedAt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" args"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".checkpoint "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"?"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" args"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // sorted by updatedAt first and the id as second"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" sortedDocuments"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" documents"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".sort"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"((a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" b) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" b"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" b"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" -"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"1"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" b"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" b"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"a"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" b"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" -"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"1"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" else"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // only return documents newer than the input document"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" filterForMinUpdatedAtAndId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" sortedDocuments"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".filter"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"<"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" minUpdatedAt) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" minUpdatedAt) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" minUpdatedAt) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // if updatedAt is equal, compare by id"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:">"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" minId) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" else"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // only return some documents in one batch"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" limitedDocs"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" filterForMinUpdatedAtAndId"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".slice"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" args"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".limit);"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // use the last document for the checkpoint"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" lastDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" limitedDocs["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"limitedDocs"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" -"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"];"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" retCheckpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" lastDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" lastDoc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".updatedAt"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" documents"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" limitedDocs"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" retCheckpoint"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" limited;"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["For examples for the other resolvers, consult the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/blob/master/examples/graphql/server/index.js",children:"GraphQL Example Project"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"rxdb-client",children:"RxDB Client"}),"\n",(0,i.jsx)(n.h4,{id:"pull-replication",children:"Pull replication"}),"\n",(0,i.jsxs)(n.p,{children:["For the pull-replication, you first need a ",(0,i.jsx)(n.code,{children:"pullQueryBuilder"}),". This is a function that gets the last replication ",(0,i.jsx)(n.code,{children:"checkpoint"})," and a ",(0,i.jsx)(n.code,{children:"limit"})," as input and returns an object with a GraphQL-query and its variables (or a promise that resolves to the same object). RxDB will use the query builder to construct what is later sent to the GraphQL endpoint."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" pullQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" limit) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The first pull does not have a checkpoint"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * so we fill it up with defaults"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"checkpoint) {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ''"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `query PullHuman($checkpoint: CheckpointInput, $limit: Int!) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" pullHuman(checkpoint: $checkpoint, limit: $limit) {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" documents {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" id"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" name"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" age"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" updatedAt"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" deleted"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" checkpoint {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" id"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" updatedAt"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }`"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" operationName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'PullHuman'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" variables"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" limit"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(n.p,{children:"With the queryBuilder, you can then setup the pull-replication."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateGraphQL } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-graphql'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateGraphQL"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // urls to the GraphQL endpoints"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" http"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/graphql'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" queryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pullQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // the queryBuilder from above"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" modifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" doc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" doc"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // (optional) modifies all pulled documents before they are handled by RxDB"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" dataPath"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" undefined"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // (optional) specifies the object path to access the document(s). Otherwise, the first result of the response data is used."})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Amount of documents that the remote will send in one request."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If the response contains less than [batchSize] documents,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * RxDB will assume there are no more changes on the backend"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * that are not replicated."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This value is the same as the limit in the pullHuman() schema."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=100]"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 50"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // headers which will be used in http requests against the server."})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Authorization"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bearer abcde...'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Options that have been inherited from the RxReplication"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" deletedField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'deleted'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" retryTime "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" waitForLeadership "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" autoStart "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h4,{id:"push-replication",children:"Push replication"}),"\n",(0,i.jsxs)(n.p,{children:["For the push-replication, you also need a ",(0,i.jsx)(n.code,{children:"queryBuilder"}),". Here, the builder receives a changed document as input which has to be send to the server. It also returns a GraphQL-Query and its data."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" pushQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" rows "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" mutation PushHuman($writeRows: [HumanInputPushRow!]) {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" pushHuman(writeRows: $writeRows) {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" id"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" name"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" age"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" updatedAt"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" deleted"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" variables"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" writeRows"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" rows"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" operationName"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'PushHuman'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" variables"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(n.p,{children:"With the queryBuilder, you can then setup the push-replication."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateGraphQL"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // urls to the GraphQL endpoints"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" http"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/graphql'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" queryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pushQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // the queryBuilder from above"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * batchSize (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Amount of document that will be pushed to the server in a single request."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * modifier (optional)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Modifies all pushed documents before they are send to the GraphQL endpoint."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Returning null will skip the document."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" modifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" doc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" doc"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Authorization"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bearer abcde...'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h4,{id:"pull-stream",children:"Pull Stream"}),"\n",(0,i.jsxs)(n.p,{children:["To create a ",(0,i.jsx)(n.strong,{children:"realtime"})," replication, you need to create a pull stream that pulls ongoing writes from the server.\nThe pull stream gets the ",(0,i.jsx)(n.code,{children:"headers"})," of the ",(0,i.jsx)(n.code,{children:"RxReplicationState"})," as input, so that it can be authenticated on the backend."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" pullStreamQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (headers) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `subscription onStream($headers: Headers) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" streamHero(headers: $headers) {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" documents {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" id,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" name,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" age,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" updatedAt,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" deleted"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" },"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" checkpoint {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" id"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" updatedAt"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" }`"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" query"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" variables"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["With the ",(0,i.jsx)(n.code,{children:"pullStreamQueryBuilder"})," you can then start a realtime replication."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateGraphQL"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // urls to the GraphQL endpoints"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" http"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'http://example.com/graphql'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ws"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'ws://example.com/subscriptions'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- The websocket has to use a different url."})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" queryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pushQueryBuilder"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Authorization"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Bearer abcde...'"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" queryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pullQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" streamQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pullStreamQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" includeWsHeaders"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" false"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Includes headers as connection parameter to Websocket."})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Websocket options that can be passed as a parameter to initialize the subscription"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Can be applied anything from the graphql-ws ClientOptions - https://the-guild.dev/graphql/ws/docs/interfaces/client.ClientOptions"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Except these parameters: 'url', 'shouldRetry', 'webSocketImpl' - locked for internal usage"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // Note: if you provide connectionParams as a wsOption, make sure it returns any necessary headers (e.g. authorization)"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // because providing your own connectionParams prevents headers from being included automatically"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" wsOptions"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { "})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" retryAttempts"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" deletedField"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'deleted'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["If it is not possible to create a websocket server on your backend, you can use any other method of pull out the ongoing events from the backend and then you can send them into ",(0,i.jsx)(n.code,{children:"RxReplicationState.emitEvent()"}),"."]})}),"\n",(0,i.jsx)(n.h3,{id:"transforming-null-to-undefined-in-optional-fields",children:"Transforming null to undefined in optional fields"}),"\n",(0,i.jsxs)(n.p,{children:["GraphQL fills up non-existent optional values with ",(0,i.jsx)(n.code,{children:"null"})," while RxDB required them to be ",(0,i.jsx)(n.code,{children:"undefined"}),".\nTherefore, if your schema contains optional properties, you have to transform the pulled data to switch out ",(0,i.jsx)(n.code,{children:"null"})," to ",(0,i.jsx)(n.code,{children:"undefined"})]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" RxGraphQLReplicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"RxDocType"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"> "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateGraphQL"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" queryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pullQueryBuilder"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" modifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (doc "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // We have to remove optional non-existent field values"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // they are set as null by GraphQL but should be undefined"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Object"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".entries"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(doc)"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".forEach"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(([k"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" v]) "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (v "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" null"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" delete"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" doc[k];"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" doc;"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h3,{id:"pullresponsemodifier",children:"pull.responseModifier"}),"\n",(0,i.jsxs)(n.p,{children:["With the ",(0,i.jsx)(n.code,{children:"pull.responseModifier"})," you can modify the whole response from the GraphQL endpoint ",(0,i.jsx)(n.strong,{children:"before"})," it is processed by RxDB.\nFor example if your endpoint is not capable of returning a valid checkpoint, but instead only returns the plain document array, you can use the ",(0,i.jsx)(n.code,{children:"responseModifier"})," to aggregate the checkpoint from the returned documents."]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" RxGraphQLReplicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"RxDocType"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"> "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateGraphQL"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" responseModifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" plainResponse"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // the exact response that was returned from the server"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" origin"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // either 'handler' if plainResponse came from the pull.handler, or 'stream' if it came from the pull.stream"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" requestCheckpoint "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// if origin==='handler', the requestCheckpoint contains the checkpoint that was send to the backend"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" ) {"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * In this example we aggregate the checkpoint from the documents array"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * that was returned from the graphql endpoint."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docs"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" plainResponse;"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" documents"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" docs"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" checkpoint"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" docs"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ==="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ?"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" requestCheckpoint "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" lastOfArray"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(docs).name"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" updatedAt"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" lastOfArray"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(docs).updatedAt"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h3,{id:"pushresponsemodifier",children:"push.responseModifier"}),"\n",(0,i.jsx)(n.p,{children:"It's also possible to modify the response of a push mutation. For example if your server returns more than the just conflicting docs:"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"graphql","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"graphql","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" PushResponse"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" conflicts: ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"Human"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" conflictMessages: ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"ReplicationConflictMessage"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"type"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" Mutation"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" # Returns a PushResponse type that contains the conflicts along with other information"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pushHuman(rows: ["}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"HumanInputPushRow"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"]): "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"PushResponse"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "rxdb"'}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" RxGraphQLReplicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"RxDocType"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"> "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateGraphQL"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" headers"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" responseModifier"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" (plainResponse) {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * In this example we aggregate the conflicting documents from a response object"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" plainResponse"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:".conflicts;"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"}"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsx)(n.h4,{id:"helper-functions",children:"Helper Functions"}),"\n",(0,i.jsxs)(n.p,{children:["RxDB provides the helper functions ",(0,i.jsx)(n.code,{children:"graphQLSchemaFromRxSchema()"}),", ",(0,i.jsx)(n.code,{children:"pullQueryBuilderFromRxSchema()"}),", ",(0,i.jsx)(n.code,{children:"pullStreamBuilderFromRxSchema()"})," and ",(0,i.jsx)(n.code,{children:"pushQueryBuilderFromRxSchema()"})," that can be used to generate handlers and schemas from the ",(0,i.jsx)(n.code,{children:"RxJsonSchema"}),". To learn how to use them, please inspect the ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/graphql",children:"GraphQL Example"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"rxgraphqlreplicationstate",children:"RxGraphQLReplicationState"}),"\n",(0,i.jsxs)(n.p,{children:["When you call ",(0,i.jsx)(n.code,{children:"myCollection.syncGraphQL()"})," it returns a ",(0,i.jsx)(n.code,{children:"RxGraphQLReplicationState"})," which can be used to subscribe to events, for debugging or other functions. It extends the ",(0,i.jsx)(n.a,{href:"/replication.html",children:"RxReplicationState"})," with some GraphQL specific methods."]}),"\n",(0,i.jsx)(n.h4,{id:"setheaders",children:".setHeaders()"}),"\n",(0,i.jsx)(n.p,{children:"Changes the headers for the replication after it has been set up."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".setHeaders"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" Authorization"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" `...`"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(n.h4,{id:"sending-cookies",children:"Sending Cookies"}),"\n",(0,i.jsxs)(n.p,{children:["The underlying fetch framework uses a ",(0,i.jsx)(n.code,{children:"same-origin"})," policy for credentials per default. That means, cookies and session data is only shared if you backend and frontend run on the same domain and port. Pass the credential parameter to ",(0,i.jsx)(n.code,{children:"include"})," cookies in requests to servers from different origins via:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsx)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".setCredentials"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'include'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})})})}),"\n",(0,i.jsxs)(n.p,{children:["or directly pass it in the ",(0,i.jsx)(n.code,{children:"replicateGraphQL"})," function:"]}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"replicateGraphQL"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" credentials"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'include'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsxs)(n.p,{children:["See ",(0,i.jsx)(n.a,{href:"https://fetch.spec.whatwg.org/#concept-request-credentials-mode",children:"the fetch spec"})," for more information about available options."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["To play around, check out the full example of the RxDB ",(0,i.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/graphql",children:"GraphQL replication with server and client"})]})})]})}function h(s={}){const{wrapper:n}={...(0,o.R)(),...s.components};return n?(0,i.jsx)(n,{...s,children:(0,i.jsx)(d,{...s})}):d(s)}},8453(s,n,e){e.d(n,{R:()=>l,x:()=>a});var r=e(6540);const i={},o=r.createContext(i);function l(s){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof s?s(n):{...n,...s}},[n,s])}function a(s){let n;return n=s.disableParentContext?"function"==typeof s.components?s.components(i):s.components||i:l(s.components),r.createElement(o.Provider,{value:n},s.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/ab919a1f.41585657.js b/docs/assets/js/ab919a1f.41585657.js
deleted file mode 100644
index ae849cdb84b..00000000000
--- a/docs/assets/js/ab919a1f.41585657.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6491],{30(e,a,i){i.r(a),i.d(a,{assets:()=>d,contentTitle:()=>l,default:()=>h,frontMatter:()=>r,metadata:()=>t,toc:()=>o});const t=JSON.parse('{"id":"articles/embedded-database","title":"Embedded Database, Real-time Speed - RxDB","description":"Unleash the power of embedded databases with RxDB. Explore real-time replication, offline access, and reactive queries for modern JavaScript apps.","source":"@site/docs/articles/embedded-database.md","sourceDirName":"articles","slug":"/articles/embedded-database.html","permalink":"/articles/embedded-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Embedded Database, Real-time Speed - RxDB","slug":"embedded-database.html","description":"Unleash the power of embedded databases with RxDB. Explore real-time replication, offline access, and reactive queries for modern JavaScript apps."},"sidebar":"tutorialSidebar","previous":{"title":"Empower Web Apps with Reactive RxDB Data-base","permalink":"/articles/data-base.html"},"next":{"title":"Supercharge Flutter Apps with the RxDB Database","permalink":"/articles/flutter-database.html"}}');var s=i(4848),n=i(8453);const r={title:"Embedded Database, Real-time Speed - RxDB",slug:"embedded-database.html",description:"Unleash the power of embedded databases with RxDB. Explore real-time replication, offline access, and reactive queries for modern JavaScript apps."},l="Using RxDB as an Embedded Database",d={},o=[{value:"What is an Embedded Database?",id:"what-is-an-embedded-database",level:2},{value:"Embedded Database in UI Applications",id:"embedded-database-in-ui-applications",level:2},{value:"Why RxDB as an Embedded Database for Real-time Applications",id:"why-rxdb-as-an-embedded-database-for-real-time-applications",level:2},{value:"Follow Up",id:"follow-up",level:2}];function c(e){const a={a:"a",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",ul:"ul",...(0,n.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a.header,{children:(0,s.jsx)(a.h1,{id:"using-rxdb-as-an-embedded-database",children:"Using RxDB as an Embedded Database"})}),"\n",(0,s.jsxs)(a.p,{children:["In modern UI applications, efficient data storage is a crucial aspect for seamless user experiences. One powerful solution for achieving this is by utilizing an embedded database. In this article, we will explore the concept of an embedded database and delve into the benefits of using ",(0,s.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"})," as an embedded database in UI applications. We will also discuss why RxDB stands out as a robust choice for real-time applications with embedded database functionality."]}),"\n",(0,s.jsx)("center",{children:(0,s.jsx)("a",{href:"https://rxdb.info/",children:(0,s.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Embedded Database",width:"220"})})}),"\n",(0,s.jsx)(a.h2,{id:"what-is-an-embedded-database",children:"What is an Embedded Database?"}),"\n",(0,s.jsxs)(a.p,{children:["An embedded database refers to a client-side database system that is integrated directly within an application. It is designed to operate within the client environment, such as a web browser or a ",(0,s.jsx)(a.a,{href:"/articles/mobile-database.html",children:"mobile"})," app. This approach eliminates the need for a separate database server and allows the database to run locally on the client device."]}),"\n",(0,s.jsx)(a.h2,{id:"embedded-database-in-ui-applications",children:"Embedded Database in UI Applications"}),"\n",(0,s.jsx)(a.p,{children:"In the context of UI applications, an embedded database serves as a local data storage solution. It enables applications to efficiently manage data, facilitate real-time updates, and enhance performance. Let's explore some of the benefits of using an embedded database compared to a traditional server database:"}),"\n",(0,s.jsxs)(a.ul,{children:["\n",(0,s.jsx)(a.li,{children:"Replicating database state becomes easier: Implementing real-time data synchronization and replication is simpler with an embedded database compared to complex REST routes. The embedded nature allows for efficient replication of the database state across multiple instances of the application."}),"\n",(0,s.jsx)(a.li,{children:"Use the database for caching: An embedded database can be utilized for caching frequently accessed data. This caching mechanism enhances performance and reduces the need for repeated network requests, resulting in faster data retrieval."}),"\n",(0,s.jsx)(a.li,{children:"Building real-time applications is easier with local data: By leveraging local data storage, real-time applications can easily update the user interface in response to data changes. This approach simplifies the development of real-time features and enhances the responsiveness of the application."}),"\n",(0,s.jsxs)(a.li,{children:["Store local data with ",(0,s.jsx)(a.a,{href:"/encryption.html",children:"encryption"}),": Embedded databases, like RxDB, offer the ability to store local data with encryption. This ensures that sensitive information remains protected even when stored locally on the client device."]}),"\n",(0,s.jsx)(a.li,{children:"Data is offline accessible: With an embedded database, data remains accessible even when the application is offline. Users can continue to interact with the application and access their data seamlessly, irrespective of their internet connectivity."}),"\n",(0,s.jsx)(a.li,{children:"Faster initial application start time: Since the data is already stored locally, there is no need for initial data fetching from a remote server. This significantly reduces the application's startup time and allows users to engage with the application more quickly."}),"\n",(0,s.jsx)(a.li,{children:"Improved scalability with local queries: Embedded databases, such as RxDB, perform queries locally on the client device instead of relying on server round-trips. This reduces latency and enhances scalability, particularly when dealing with large datasets or high query volumes."}),"\n",(0,s.jsxs)(a.li,{children:["Seamless integration with JavaScript frameworks: Embedded databases, including RxDB, integrate seamlessly with popular JavaScript frameworks like Angular, React.js, ",(0,s.jsx)(a.a,{href:"/articles/vue-database.html",children:"Vue.js"}),", and Svelte. This compatibility allows developers to leverage the capabilities of these frameworks while benefiting from embedded database functionality."]}),"\n",(0,s.jsx)(a.li,{children:"Running queries locally has low latency: With an embedded database, queries are executed locally on the client device, resulting in minimal latency. This improves the overall performance and responsiveness of the application."}),"\n",(0,s.jsx)(a.li,{children:"Data is portable and always accessible by the user: Embedded databases enable data portability, allowing users to seamlessly transition between devices while maintaining their data and application state. This ensures that data is always accessible and available to the user."}),"\n",(0,s.jsxs)(a.li,{children:["Using a ",(0,s.jsx)(a.a,{href:"/articles/local-database.html",children:"local database"})," for state management: Instead of relying on additional state management libraries like Redux or NgRx, an embedded database can be used for local state management. This simplifies state management and ensures data consistency within the application."]}),"\n"]}),"\n",(0,s.jsx)(a.h2,{id:"why-rxdb-as-an-embedded-database-for-real-time-applications",children:"Why RxDB as an Embedded Database for Real-time Applications"}),"\n",(0,s.jsx)(a.p,{children:"RxDB is a JavaScript-based embedded database that offers numerous advantages for building real-time applications. Let's explore why RxDB is a compelling choice:"}),"\n",(0,s.jsxs)(a.ul,{children:["\n",(0,s.jsxs)(a.li,{children:[(0,s.jsx)(a.a,{href:"/rx-query.html",children:"Observable Queries"})," (RxJS): RxDB leverages the power of Observables through RxJS, enabling developers to create queries that automatically update the user interface on data changes. This reactive approach simplifies UI updates and ensures real-time synchronization of data."]}),"\n",(0,s.jsxs)(a.li,{children:[(0,s.jsx)(a.a,{href:"/articles/json-database.html",children:"NoSQL JSON Documents"})," for UIs: RxDB utilizes NoSQL (JSON) documents as its data model, aligning seamlessly with the requirements of modern UI development. JavaScript's native support for JSON objects makes NoSQL documents a natural fit for UI-driven applications."]}),"\n",(0,s.jsx)(a.li,{children:"Better TypeScript Support Compared to SQL: RxDB's NoSQL approach provides excellent TypeScript support. The flexibility of working with JSON objects enables robust typing and enhanced development experiences, ensuring type safety and reducing runtime errors."}),"\n",(0,s.jsxs)(a.li,{children:[(0,s.jsx)(a.a,{href:"/rx-document.html",children:"Observable Document Fields"}),": RxDB allows developers to observe individual fields within documents. This granularity enables efficient tracking of specific data changes and facilitates targeted UI updates, enhancing performance and responsiveness."]}),"\n",(0,s.jsx)(a.li,{children:"Made in JavaScript, Optimized for JavaScript Applications: Being built entirely in JavaScript, RxDB is optimized for JavaScript applications. It leverages JavaScript's capabilities and integrates seamlessly with JavaScript frameworks and libraries, making it a natural choice for JavaScript developers."}),"\n",(0,s.jsxs)(a.li,{children:["Optimized Observed Queries with the ",(0,s.jsx)(a.a,{href:"https://github.com/pubkey/event-reduce",children:"EventReduce Algorithm"}),": RxDB incorporates the EventReduce algorithm to optimize observed queries. This algorithm reduces the number of emitted events during query execution, resulting in enhanced query performance and reduced overhead."]}),"\n",(0,s.jsx)(a.li,{children:"Built-in Multi-tab Support: RxDB provides built-in multi-tab support, allowing multiple instances of an application to share and synchronize data seamlessly. This feature enables collaborative and real-time scenarios across multiple browser tabs or windows."}),"\n",(0,s.jsxs)(a.li,{children:["Handling of Schema Changes across Multiple Client Devices: With RxDB, handling schema changes across multiple client devices becomes straightforward. RxDB's schema ",(0,s.jsx)(a.a,{href:"/migration-schema.html",children:"migration capabilities"})," ensure that applications can seamlessly adapt to evolving data structures, providing a consistent experience across different devices."]}),"\n",(0,s.jsx)(a.li,{children:"Storing Documents Compressed: RxDB offers the ability to store documents in a compressed format. This reduces the storage footprint and improves performance, especially when dealing with large datasets."}),"\n",(0,s.jsxs)(a.li,{children:["Flexible Storage Layer and Cross-platform Compatibility: RxDB provides a flexible storage layer that can be reused across various platforms, including ",(0,s.jsx)(a.a,{href:"/electron-database.html",children:"Electron.js"}),", ",(0,s.jsx)(a.a,{href:"/react-native-database.html",children:"React Native"}),", hybrid apps (via Capacitor.js), and browsers. This cross-platform compatibility simplifies development and enables code reuse across different environments."]}),"\n",(0,s.jsxs)(a.li,{children:["Replication Algorithm for Backend Compatibility: RxDB's replication algorithm is open-source and can be made compatible with various backend solutions, such as self-hosted servers, Firebase, ",(0,s.jsx)(a.a,{href:"/replication-couchdb.html",children:"CouchDB"}),", NATS, WebSockets, and more. This flexibility allows developers to choose their preferred backend infrastructure while benefiting from RxDB's embedded database capabilities."]}),"\n"]}),"\n",(0,s.jsx)("center",{children:(0,s.jsx)("a",{href:"https://rxdb.info/",children:(0,s.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Embedded Database",width:"220"})})}),"\n",(0,s.jsx)(a.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,s.jsxs)(a.p,{children:["To further explore ",(0,s.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"})," and leverage its capabilities as an embedded database, the following resources can be helpful:"]}),"\n",(0,s.jsxs)(a.ul,{children:["\n",(0,s.jsxs)(a.li,{children:[(0,s.jsx)(a.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB GitHub Repository"}),": Visit the official GitHub repository of RxDB to access the source code, documentation, and community support."]}),"\n",(0,s.jsxs)(a.li,{children:[(0,s.jsx)(a.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),": Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects."]}),"\n"]}),"\n",(0,s.jsxs)(a.p,{children:["By utilizing ",(0,s.jsx)(a.a,{href:"https://rxdb.info/",children:"RxDB"})," as an embedded database in UI applications, developers can harness the power of efficient data management, real-time updates, and enhanced user experiences. RxDB's features and benefits make it a compelling choice for building modern, responsive, and scalable applications."]})]})}function h(e={}){const{wrapper:a}={...(0,n.R)(),...e.components};return a?(0,s.jsx)(a,{...e,children:(0,s.jsx)(c,{...e})}):c(e)}},8453(e,a,i){i.d(a,{R:()=>r,x:()=>l});var t=i(6540);const s={},n=t.createContext(s);function r(e){const a=t.useContext(n);return t.useMemo(function(){return"function"==typeof e?e(a):{...a,...e}},[a,e])}function l(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:r(e.components),t.createElement(n.Provider,{value:a},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/aba21aa0.6f50d3cb.js b/docs/assets/js/aba21aa0.6f50d3cb.js
deleted file mode 100644
index b91a605de93..00000000000
--- a/docs/assets/js/aba21aa0.6f50d3cb.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5742],{7093(s){s.exports=JSON.parse('{"name":"docusaurus-plugin-content-docs","id":"default"}')}}]);
\ No newline at end of file
diff --git a/docs/assets/js/ac62b32d.0f37c11a.js b/docs/assets/js/ac62b32d.0f37c11a.js
deleted file mode 100644
index b078b6e4382..00000000000
--- a/docs/assets/js/ac62b32d.0f37c11a.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9192],{3228(e,n,s){s.r(n),s.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>t,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"replication-websocket","title":"Websocket Replication","description":"With the websocket replication plugin, you can spawn a websocket server from a RxDB database in Node.js and replicate with it.","source":"@site/docs/replication-websocket.md","sourceDirName":".","slug":"/replication-websocket.html","permalink":"/replication-websocket.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Websocket Replication","slug":"replication-websocket.html"},"sidebar":"tutorialSidebar","previous":{"title":"GraphQL Replication","permalink":"/replication-graphql.html"},"next":{"title":"CouchDB Replication","permalink":"/replication-couchdb.html"}}');var i=s(4848),o=s(8453);const t={title:"Websocket Replication",slug:"replication-websocket.html"},a="Websocket Replication",l={},c=[{value:"Starting the Websocket Server",id:"starting-the-websocket-server",level:2},{value:"Connect to the Websocket Server",id:"connect-to-the-websocket-server",level:2},{value:"Customize",id:"customize",level:2}];function d(e){const n={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",strong:"strong",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"websocket-replication",children:"Websocket Replication"})}),"\n",(0,i.jsx)(n.p,{children:"With the websocket replication plugin, you can spawn a websocket server from a RxDB database in Node.js and replicate with it."}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["The websocket replication plugin does not have any concept for authentication or permission handling. It is designed to create an easy ",(0,i.jsx)(n.strong,{children:"server-to-server"})," replication. It is ",(0,i.jsx)(n.strong,{children:"not"})," made for client-server replication. Make a pull request if you need that feature."]})}),"\n",(0,i.jsx)(n.h2,{id:"starting-the-websocket-server",children:"Starting the Websocket Server"}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" startWebsocketServer"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-websocket'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// create a RxDatabase like normal"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// start a websocket server"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" serverState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" startWebsocketServer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" database"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myDatabase"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" port"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1337"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" path"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '/socket'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// stop the server"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" serverState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".close"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(n.h2,{id:"connect-to-the-websocket-server",children:"Connect to the Websocket Server"}),"\n",(0,i.jsx)(n.p,{children:"The replication has to be started once for each collection that you want to replicate."}),"\n",(0,i.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" replicateWithWebsocketServer"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication-websocket'"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// start the replication"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" replicateWithWebsocketServer"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * To make the replication work,"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * the client collection name must be equal"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * to the server collection name."})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" myRxCollection"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" url"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'ws://localhost:1337/socket'"})]}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(n.span,{"data-line":"",children:(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// stop the replication"})}),"\n",(0,i.jsxs)(n.span,{"data-line":"",children:[(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" replicationState"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cancel"}),(0,i.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(n.h2,{id:"customize",children:"Customize"}),"\n",(0,i.jsxs)(n.p,{children:["We use the ",(0,i.jsx)(n.a,{href:"https://www.npmjs.com/package/ws",children:"ws"})," npm library, so you can use all optional configuration provided by it.\nThis is especially important to improve performance by opting in of some optional settings."]})]})}function h(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>t,x:()=>a});var r=s(6540);const i={},o=r.createContext(i);function t(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:t(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/ad16b3ea.bade954e.js b/docs/assets/js/ad16b3ea.bade954e.js
deleted file mode 100644
index 606d0caf007..00000000000
--- a/docs/assets/js/ad16b3ea.bade954e.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8674],{3247(e,s,n){n.d(s,{g:()=>i});var r=n(4848);function i(e){const s=[];let n=null;return e.children.forEach(e=>{e.props.id?(n&&s.push(n),n={headline:e,paragraphs:[]}):n&&n.paragraphs.push(e)}),n&&s.push(n),(0,r.jsx)("div",{style:o.stepsContainer,children:s.map((e,s)=>(0,r.jsxs)("div",{style:o.stepWrapper,children:[(0,r.jsxs)("div",{style:o.stepIndicator,children:[(0,r.jsx)("div",{style:o.stepNumber,children:s+1}),(0,r.jsx)("div",{style:o.verticalLine})]}),(0,r.jsxs)("div",{style:o.stepContent,children:[(0,r.jsx)("div",{children:e.headline}),e.paragraphs.map((e,s)=>(0,r.jsx)("div",{style:o.item,children:e},s))]})]},s))})}const o={stepsContainer:{display:"flex",flexDirection:"column"},stepWrapper:{display:"flex",alignItems:"stretch",marginBottom:"1.5rem",position:"relative",minWidth:0},stepIndicator:{position:"relative",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"flex-start",width:"33px",marginRight:"1rem",minWidth:0},stepNumber:{width:"33px",height:"33px",borderRadius:"50%",backgroundColor:"var(--color-top)",color:"#fff",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:"bold",marginTop:-4},verticalLine:{position:"absolute",top:29,bottom:"0",left:"50%",width:"1px",background:"linear-gradient(to bottom, var(--color-top) 0%, var(--color-top) 80%, rgba(0,0,0,0) 100%)",transform:"translateX(-50%)"},stepContent:{flex:1,minWidth:0,overflowWrap:"break-word"},item:{marginTop:"0.5rem"}}},5045(e,s,n){n.r(s),n.d(s,{assets:()=>d,contentTitle:()=>t,default:()=>p,frontMatter:()=>l,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"dev-mode","title":"Development Mode","description":"Enable checks & validations with RxDB Dev Mode. Ensure proper API use, readable errors, and schema validation during development. Avoid in production.","source":"@site/docs/dev-mode.md","sourceDirName":".","slug":"/dev-mode.html","permalink":"/dev-mode.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Development Mode","slug":"dev-mode.html","description":"Enable checks & validations with RxDB Dev Mode. Ensure proper API use, readable errors, and schema validation during development. Avoid in production."},"sidebar":"tutorialSidebar","previous":{"title":"Installation","permalink":"/install.html"},"next":{"title":"TypeScript Setup","permalink":"/tutorials/typescript.html"}}');var i=n(4848),o=n(8453),a=n(3247);const l={title:"Development Mode",slug:"dev-mode.html",description:"Enable checks & validations with RxDB Dev Mode. Ensure proper API use, readable errors, and schema validation during development. Avoid in production."},t="Dev Mode",d={},c=[{value:"Import the dev-mode Plugin",id:"import-the-dev-mode-plugin",level:3},{value:"Add the Plugin to RxDB",id:"add-the-plugin-to-rxdb",level:2},{value:"Usage with Node.js",id:"usage-with-nodejs",level:2},{value:"Usage with Angular",id:"usage-with-angular",level:2},{value:"Usage with webpack",id:"usage-with-webpack",level:2},{value:"Disable the dev-mode warning",id:"disable-the-dev-mode-warning",level:2},{value:"Disable the tracking iframe",id:"disable-the-tracking-iframe",level:2}];function h(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"dev-mode",children:"Dev Mode"})}),"\n",(0,i.jsx)(s.p,{children:"The dev-mode plugin adds many checks and validations to RxDB.\nThis ensures that you use the RxDB API properly and so the dev-mode plugin should always be used when\nusing RxDB in development mode."}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Adds readable error messages."}),"\n",(0,i.jsxs)(s.li,{children:["Ensures that ",(0,i.jsx)(s.code,{children:"readonly"})," JavaScript objects are not accidentally mutated."]}),"\n",(0,i.jsxs)(s.li,{children:["Adds validation check for validity of schemas, queries, ",(0,i.jsx)(s.a,{href:"/orm.html",children:"ORM"})," methods and document fields.","\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Notice that the ",(0,i.jsx)(s.code,{children:"dev-mode"})," plugin does not perform schema checks against the data see ",(0,i.jsx)(s.a,{href:"/schema-validation.html",children:"schema validation"})," for that."]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.admonition,{type:"warning",children:(0,i.jsxs)(s.p,{children:["The dev-mode plugin will increase your build size and decrease the performance. It must ",(0,i.jsx)(s.strong,{children:"always"})," be used in development. You should ",(0,i.jsx)(s.strong,{children:"never"})," use it in production."]})}),"\n",(0,i.jsxs)(a.g,{children:[(0,i.jsx)(s.h3,{id:"import-the-dev-mode-plugin",children:"Import the dev-mode Plugin"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBDevModePlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/dev-mode'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]})]})})}),(0,i.jsx)(s.h2,{id:"add-the-plugin-to-rxdb",children:"Add the Plugin to RxDB"}),(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBDevModePlugin);"})]})})})})]}),"\n",(0,i.jsx)(s.h2,{id:"usage-with-nodejs",children:"Usage with Node.js"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createDb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"process"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"env"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"NODE_ENV"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" !=="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "production"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'rxdb/plugins/dev-mode'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" module "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"module"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".RxDBDevModePlugin)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"( "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"usage-with-angular",children:"Usage with Angular"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { isDevMode } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" '@angular/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createDb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"isDevMode"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()){"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'rxdb/plugins/dev-mode'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" module "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"module"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".RxDBDevModePlugin)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"( "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ..."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"usage-with-webpack",children:"Usage with webpack"}),"\n",(0,i.jsxs)(s.p,{children:["In the ",(0,i.jsx)(s.code,{children:"webpack.config.js"}),":"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"module"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"exports"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" entry"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" './src/index.ts'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" plugins"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // set a global variable that can be accessed during runtime"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" webpack"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".DefinePlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({ MODE"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" JSON"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".stringify"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"production"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") })"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"In your source code:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"declare"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" var"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" MODE"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'production'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" |"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'development'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createDb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"MODE"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ==="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'development'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'rxdb/plugins/dev-mode'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".then"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" module "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" addRxPlugin"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"module"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".RxDBDevModePlugin)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"( "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ..."})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"disable-the-dev-mode-warning",children:"Disable the dev-mode warning"}),"\n",(0,i.jsxs)(s.p,{children:["When the dev-mode is enabled, it will print a ",(0,i.jsx)(s.code,{children:"console.warn()"})," message to the console so that you do not accidentally use the dev-mode in production. To disable this warning you can call the ",(0,i.jsx)(s.code,{children:"disableWarnings()"})," function."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { disableWarnings } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/dev-mode'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"disableWarnings"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,i.jsx)(s.h2,{id:"disable-the-tracking-iframe",children:"Disable the tracking iframe"}),"\n",(0,i.jsxs)(s.p,{children:["When used in localhost and in the browser, the dev-mode plugin can add a tracking iframe to the DOM. This is used to track the effectiveness of marketing efforts of RxDB.\nIf you have ",(0,i.jsx)(s.a,{href:"/premium/",children:"premium access"})," and want to disable this iframe, you can call ",(0,i.jsx)(s.code,{children:"setPremiumFlag()"})," before creating the database."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { setPremiumFlag } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/shared'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"setPremiumFlag"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})})]})}function p(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/ae2c2832.694f423a.js b/docs/assets/js/ae2c2832.694f423a.js
deleted file mode 100644
index 94d7544022a..00000000000
--- a/docs/assets/js/ae2c2832.694f423a.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3321],{6839(e,a,s){s.r(a),s.d(a,{default:()=>i});var t=s(544),l=s(4848);function i(){return(0,t.default)({sem:{id:"gads",metaTitle:"The local Database for Svelte Apps",appName:"Svelte",title:(0,l.jsxs)(l.Fragment,{children:["The easiest way to ",(0,l.jsx)("b",{children:"store"})," and ",(0,l.jsx)("b",{children:"sync"})," Data in Svelte"]}),iconUrl:"/files/icons/svelte.svg"}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/b0889a22.eeabb12f.js b/docs/assets/js/b0889a22.eeabb12f.js
deleted file mode 100644
index f278c620df8..00000000000
--- a/docs/assets/js/b0889a22.eeabb12f.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[1107],{3519(e,n,s){s.r(n),s.d(n,{assets:()=>r,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"cleanup","title":"Cleanup","description":"Optimize storage and speed up queries with RxDB\'s Cleanup Plugin, automatically removing old deleted docs while preserving replication states.","source":"@site/docs/cleanup.md","sourceDirName":".","slug":"/cleanup.html","permalink":"/cleanup.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Cleanup","slug":"cleanup.html","description":"Optimize storage and speed up queries with RxDB\'s Cleanup Plugin, automatically removing old deleted docs while preserving replication states."},"sidebar":"tutorialSidebar","previous":{"title":"Local Documents","permalink":"/rx-local-document.html"},"next":{"title":"Backup","permalink":"/backup.html"}}');var l=s(4848),a=s(8453);const o={title:"Cleanup",slug:"cleanup.html",description:"Optimize storage and speed up queries with RxDB's Cleanup Plugin, automatically removing old deleted docs while preserving replication states."},t="\ud83e\uddf9 Cleanup",r={},c=[{value:"Installation",id:"installation",level:2},{value:"Create a database with cleanup options",id:"create-a-database-with-cleanup-options",level:2},{value:"Calling cleanup manually",id:"calling-cleanup-manually",level:2},{value:"Using the cleanup plugin to empty a collection",id:"using-the-cleanup-plugin-to-empty-a-collection",level:2},{value:"FAQ",id:"faq",level:2}];function d(e){const n={code:"code",figure:"figure",h1:"h1",h2:"h2",header:"header",p:"p",pre:"pre",span:"span",...(0,a.R)(),...e.components},{Details:s}=n;return s||function(e,n){throw new Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,l.jsxs)(l.Fragment,{children:[(0,l.jsx)(n.header,{children:(0,l.jsx)(n.h1,{id:"-cleanup",children:"\ud83e\uddf9 Cleanup"})}),"\n",(0,l.jsx)(n.p,{children:"To make the replication work, and for other reasons, RxDB has to keep deleted documents in storage so that it can replicate their deletion state.\nThis ensures that when a client is offline, the deletion state is still known and can be replicated with the backend when the client goes online again."}),"\n",(0,l.jsx)(n.p,{children:"Keeping too many deleted documents in the storage, can slow down queries or fill up too much disc space.\nWith the cleanup plugin, RxDB will run cleanup cycles that clean up deleted documents when it can be done safely."}),"\n",(0,l.jsx)(n.h2,{id:"installation",children:"Installation"}),"\n",(0,l.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,l.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,l.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBCleanupPlugin } "}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/cleanup'"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBCleanupPlugin);"})]})]})})}),"\n",(0,l.jsx)(n.h2,{id:"create-a-database-with-cleanup-options",children:"Create a database with cleanup options"}),"\n",(0,l.jsxs)(n.p,{children:["You can set a specific cleanup policy when a ",(0,l.jsx)(n.code,{children:"RxDatabase"})," is created. For most use cases, the defaults should be ok."]}),"\n",(0,l.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,l.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,l.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" cleanupPolicy"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The minimum time in milliseconds for how long"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * a document has to be deleted before it is"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * purged by the cleanup."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=one month]"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" minimumDeletedTime"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 24"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 31"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // one month,"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * The minimum amount of that that the RxCollection must have existed."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This ensures that at the initial page load, more important"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * tasks are not slowed down because a cleanup process is running."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=60 seconds]"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" minimumCollectionAge"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // 60 seconds"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * After the initial cleanup is done,"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * a new cleanup is started after [runEach] milliseconds "})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=5 minutes]"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" runEach"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 1000"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 60"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:" *"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" // 5 minutes"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If set to true,"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * RxDB will await all running replications"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * to not have a replication cycle running."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This ensures we do not remove deleted documents"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * when they might not have already been replicated."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=true]"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" awaitReplicationsInSync"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" /**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * If true, it will only start the cleanup"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * when the current instance is also the leader."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * This ensures that when RxDB is used in multiInstance mode,"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * only one instance will start the cleanup."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * [default=true]"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" waitForLeadership"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" true"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,l.jsx)(n.h2,{id:"calling-cleanup-manually",children:"Calling cleanup manually"}),"\n",(0,l.jsxs)(n.p,{children:["You can manually run a cleanup per collection by calling ",(0,l.jsx)(n.code,{children:"RxCollection.cleanup()"}),"."]}),"\n",(0,l.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,l.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,l.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,l.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Manually run the cleanup with the"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * minimumDeletedTime from the cleanupPolicy."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cleanup"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Overwrite the minimumDeletedTime"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * be setting it explicitly (time in milliseconds)"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cleanup"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"1000"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:" "}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"/**"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * Purge all deleted documents no"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * matter when they where deleted"})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" * by setting minimumDeletedTime to zero."})}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:" */"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cleanup"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,l.jsx)(n.h2,{id:"using-the-cleanup-plugin-to-empty-a-collection",children:"Using the cleanup plugin to empty a collection"}),"\n",(0,l.jsxs)(n.p,{children:["When you have a collection with documents and you want to empty it by purging all documents, the recommended way is to call ",(0,l.jsx)(n.code,{children:"myRxCollection.remove()"}),". However, this will destroy the JavaScript class of the collection and stop all listeners and observables.\nSometimes the better option might be to manually delete all documents and then use the cleanup plugin to purge the deleted documents:"]}),"\n",(0,l.jsx)(n.figure,{"data-rehype-pretty-code-figure":"",children:(0,l.jsx)(n.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,l.jsxs)(n.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// delete all documents"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,l.jsx)(n.span,{"data-line":"",children:(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-comment)"},children:"// purge all deleted documents"})}),"\n",(0,l.jsxs)(n.span,{"data-line":"",children:[(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:" myRxCollection"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-function)"},children:".cleanup"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,l.jsx)(n.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})]})})}),"\n",(0,l.jsx)(n.h2,{id:"faq",children:"FAQ"}),"\n",(0,l.jsxs)(s,{children:[(0,l.jsx)("summary",{children:"When does the cleanup run"}),(0,l.jsx)("div",{children:(0,l.jsxs)(n.p,{children:["The cleanup cycles are optimized to run only when the database is idle and it is unlikely that another database interaction performance will be decreased in the meantime. For example, by default, the cleanup does not run in the first 60 seconds of the creation of a collection to ensure the initial page load of your website does not slow down. Also, we use mechanisms like the ",(0,l.jsx)(n.code,{children:"requestIdleCallback()"})," API to improve the correct timing of the cleanup cycle."]})})]})]})}function h(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,l.jsx)(n,{...e,children:(0,l.jsx)(d,{...e})}):d(e)}},8453(e,n,s){s.d(n,{R:()=>o,x:()=>t});var i=s(6540);const l={},a=i.createContext(l);function o(e){const n=i.useContext(a);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function t(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(l):e.components||l:o(e.components),i.createElement(a.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/b2653a00.0fc762c0.js b/docs/assets/js/b2653a00.0fc762c0.js
deleted file mode 100644
index e5f4ef9b1f6..00000000000
--- a/docs/assets/js/b2653a00.0fc762c0.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3588],{4968(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/vue-database","title":"RxDB as a Database in a Vue.js Application","description":"Level up your Vue projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser.","source":"@site/docs/articles/vue-database.md","sourceDirName":"articles","slug":"/articles/vue-database.html","permalink":"/articles/vue-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB as a Database in a Vue.js Application","slug":"vue-database.html","description":"Level up your Vue projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser."},"sidebar":"tutorialSidebar","previous":{"title":"React Native Encryption and Encrypted Database/Storage","permalink":"/articles/react-native-encryption.html"},"next":{"title":"IndexedDB Database in Vue Apps - The Power of RxDB","permalink":"/articles/vue-indexeddb.html"}}');var i=n(4848),a=n(8453);const o={title:"RxDB as a Database in a Vue.js Application",slug:"vue-database.html",description:"Level up your Vue projects with RxDB. Build real-time, resilient, and responsive apps powered by a reactive NoSQL database right in the browser."},t="RxDB as a Database in a Vue Application",l={},c=[{value:"Why Vue Applications Need a Database",id:"why-vue-applications-need-a-database",level:2},{value:"Introducing RxDB as a Database Solution",id:"introducing-rxdb-as-a-database-solution",level:2},{value:"RxDB vs. Other Vue Database Options",id:"rxdb-vs-other-vue-database-options",level:3},{value:"Getting Started with RxDB",id:"getting-started-with-rxdb",level:2},{value:"Installation",id:"installation",level:3},{value:"Creating and Configuring Your Database",id:"creating-and-configuring-your-database",level:2},{value:"Vue Reactivity and RxDB Observables",id:"vue-reactivity-and-rxdb-observables",level:2},{value:"Different RxStorage Layers for RxDB",id:"different-rxstorage-layers-for-rxdb",level:2},{value:"Synchronizing Data with RxDB between Clients and Servers",id:"synchronizing-data-with-rxdb-between-clients-and-servers",level:2},{value:"Advanced RxDB Features and Techniques",id:"advanced-rxdb-features-and-techniques",level:2},{value:"Offline-First Approach",id:"offline-first-approach",level:3},{value:"Observable Queries and Change Streams",id:"observable-queries-and-change-streams",level:3},{value:"Encryption of Local Data",id:"encryption-of-local-data",level:3},{value:"Indexing and Performance Optimization",id:"indexing-and-performance-optimization",level:3},{value:"JSON Key Compression",id:"json-key-compression",level:3},{value:"Multi-Tab Support",id:"multi-tab-support",level:3},{value:"Best Practices for Using RxDB in Vue",id:"best-practices-for-using-rxdb-in-vue",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"rxdb-as-a-database-in-a-vue-application",children:"RxDB as a Database in a Vue Application"})}),"\n",(0,i.jsxs)(s.p,{children:["In the modern web ecosystem, ",(0,i.jsx)(s.a,{href:"https://vuejs.org/",children:"Vue"})," has become a leading choice for building highly performant, reactive single-page applications (SPAs). However, while Vue excels at managing and updating the user interface, robust and efficient data handling also plays a pivotal role in delivering a great user experience. Enter ",(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"}),", a reactive JavaScript database that runs in the browser (and beyond), offering significant capabilities such as offline-first data handling, real-time synchronization, and straightforward integration with Vue's reactivity system."]}),"\n",(0,i.jsx)(s.p,{children:"This article explores how RxDB works, why it's a perfect match for Vue, and how you can leverage it to build more engaging, performant, and data-resilient Vue applications."}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"/files/logo/rxdb_javascript_database.svg",alt:"JavaScript Vue Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"why-vue-applications-need-a-database",children:"Why Vue Applications Need a Database"}),"\n",(0,i.jsx)(s.p,{children:"Vue is renowned for its lightweight core and flexible architecture centered around reactive state management and reusable components. However, modern Vue applications often require:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline Capabilities:"})," Allowing users to continue working even without internet access."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Real-Time Updates:"})," Keeping UI data in sync with changes as they occur, whether locally or from other connected clients."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Improved Performance:"})," Reducing server round trips and leveraging local storage for faster data operations."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Scalable Data Handling:"})," Managing increasingly large datasets or complex queries right in the browser."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"While you can store data in Vuex/Pinia stores or via direct AJAX calls, these solutions may not suffice when your application demands a full-featured offline-first database or complex synchronization with a server. RxDB addresses these needs with a dedicated, reactive, browser-based database that pairs seamlessly with Vue's reactivity system."}),"\n",(0,i.jsx)(s.h2,{id:"introducing-rxdb-as-a-database-solution",children:"Introducing RxDB as a Database Solution"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB - short for Reactive Database - is built on the principle of combining ",(0,i.jsx)(s.a,{href:"/articles/in-memory-nosql-database.html",children:"NoSQL database"})," capabilities with reactive programming. It runs inside your client-side environment (browser, ",(0,i.jsx)(s.a,{href:"/nodejs-database.html",children:"Node.js"}),", or ",(0,i.jsx)(s.a,{href:"/articles/mobile-database.html",children:"mobile devices"}),") and provides:"]}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Real-Time Reactivity"}),": Automatically updates subscribed components whenever data changes."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline-First Approach"}),": Stores data locally and syncs with the server when online connectivity is restored."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Data Replication"}),": Effortlessly keeps data synchronized across multiple tabs, devices, or server instances."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Multi-Tab Support"}),": Seamlessly propagates changes to all open tabs in the user's ",(0,i.jsx)(s.a,{href:"/articles/browser-database.html",children:"browser"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Observable Queries"}),": Automatically refresh the result set when documents in your queried collection change."]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"rxdb-vs-other-vue-database-options",children:"RxDB vs. Other Vue Database Options"}),"\n",(0,i.jsx)(s.p,{children:"Compared to traditional approaches - like raw IndexedDB or local storage - RxDB adds a powerful, reactive layer that simplifies your data flow. While tools like Vuex or Pinia are great for state management, they are not fully fledged databases with features like replication, conflict resolution, and offline persistence. RxDB bridges the gap by providing an integrated data handling solution tailor-made for modern, data-intensive Vue applications."}),"\n",(0,i.jsx)(s.h2,{id:"getting-started-with-rxdb",children:"Getting Started with RxDB"}),"\n",(0,i.jsx)(s.p,{children:"Let's break down the essentials for using RxDB within a Vue application."}),"\n",(0,i.jsx)(s.h3,{id:"installation",children:"Installation"}),"\n",(0,i.jsx)(s.p,{children:"You can install RxDB (and RxJS, which it depends on) via npm or yarn:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"bash","data-theme":"css-variables",children:(0,i.jsx)(s.code,{"data-language":"bash","data-theme":"css-variables",style:{display:"grid"},children:(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"npm"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" install"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxdb"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string)"},children:" rxjs"})]})})})}),"\n",(0,i.jsx)(s.h2,{id:"creating-and-configuring-your-database",children:"Creating and Configuring Your Database"}),"\n",(0,i.jsxs)(s.p,{children:["Within your Vue project, you can set up an RxDB instance in a dedicated file or a Vue plugin. Below is an example using ",(0,i.jsx)(s.a,{href:"/articles/localstorage.html",children:"Localstorage"})," as the storage engine:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// db.js"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myPassword'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional encryption password"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // multi-tab support"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" eventReduce"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optimize event handling"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" hero"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'hero schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" healthpoints"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"After creating the RxDB instance, you can share it across your application (for example, by providing it in a plugin or a global property in Vue)."}),"\n",(0,i.jsx)(s.h2,{id:"vue-reactivity-and-rxdb-observables",children:"Vue Reactivity and RxDB Observables"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB queries return RxJS observables (",(0,i.jsx)(s.code,{children:".$"}),"). Vue can automatically update components when data changes if you manually subscribe and store results in Vue refs/reactive objects, or if you use RxDB's ",(0,i.jsx)(s.a,{href:"/reactivity.html",children:"custom reactivity for Vue"}),"."]}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.strong,{children:"Example with Vue 3 Composition API:"})}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// HeroList.vue"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"script"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" setup"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"import { ref"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" onMounted } from 'vue';"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"import { initDatabase } from '@/db';"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"const heroes = ref([]);"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"let db;"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"onMounted(async () => {"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Subscribe to an RxDB query"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".hero"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" healthpoints"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { $gt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" .$ "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// the dot-$ is an observable that emits whenever the query results change"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((newHeroes) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" heroes"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".value "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" newHeroes;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"script"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"template"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"ul"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"li"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" v-for"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"hero in heroes"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:' :key="hero.id">'})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {{ hero.name }} - HP: {{ hero.healthpoints }}"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" "})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:""})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"different-rxstorage-layers-for-rxdb",children:"Different RxStorage Layers for RxDB"}),"\n",(0,i.jsx)(s.p,{children:'RxDB supports multiple storage backends - called "RxStorage layers" - giving you flexibility in how data is persisted:'}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"}),": Uses the browsers localstorage API."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),": Direct usage of native IndexedDB."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"}),": Uses the File System Access API for even faster storage in modern browsers."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-memory.html",children:"Memory RxStorage"}),": Stores data in memory, ideal for tests or ephemeral data."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"}),": Runs SQLite, which can be compiled to WebAssembly for the browser. While possible, it typically carries a larger bundle size compared to native browser APIs like ",(0,i.jsx)(s.a,{href:"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html",children:"IndexedDB or OPFS"}),"."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"Choose the storage option that best aligns with your Vue application's requirements for performance, persistence, and platform compatibility."}),"\n",(0,i.jsx)(s.h2,{id:"synchronizing-data-with-rxdb-between-clients-and-servers",children:"Synchronizing Data with RxDB between Clients and Servers"}),"\n",(0,i.jsx)(s.p,{children:"RxDB champions an offline-first approach: data is kept locally so that your Vue app remains usable, even without internet. When connectivity is restored, RxDB ensures your local changes synchronize to the server, resolving conflicts as necessary."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/database-replication.png",alt:"database replication",width:"200"})}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/articles/realtime-database.html",children:"Real-Time Synchronization"}),": With RxDB's replication plugins, any local change can be instantly pushed to a remote endpoint while pulling down remote changes to ensure consistency."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Conflict Resolution"}),": In multi-user scenarios, conflicts may arise if two clients update the same document simultaneously. RxDB provides hooks to handle and resolve these gracefully."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Scalable Architecture"}),": By reducing reliance on continuous server requests, you can lighten server load and deliver a more responsive user experience."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"advanced-rxdb-features-and-techniques",children:"Advanced RxDB Features and Techniques"}),"\n",(0,i.jsx)(s.h3,{id:"offline-first-approach",children:"Offline-First Approach"}),"\n",(0,i.jsx)(s.p,{children:"Vue applications can seamlessly function offline by leveraging RxDB's local database storage. The moment the network is restored, all unsynced data is pushed to the server. This capability is particularly beneficial for Progressive Web Apps (PWAs) and scenarios with spotty connectivity."}),"\n",(0,i.jsx)(s.h3,{id:"observable-queries-and-change-streams",children:"Observable Queries and Change Streams"}),"\n",(0,i.jsx)(s.p,{children:"Beyond simply returning data, RxDB queries emit observables that respond to any change in the underlying documents. This real-time approach can drastically simplify state management, since updates flow directly into your Vue components without additional manual wiring."}),"\n",(0,i.jsx)(s.h3,{id:"encryption-of-local-data",children:"Encryption of Local Data"}),"\n",(0,i.jsxs)(s.p,{children:["For applications handling sensitive information, RxDB supports ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"encryption"})," of local data. Your data is stored securely in the browser, protecting it from unauthorized access."]}),"\n",(0,i.jsx)(s.h3,{id:"indexing-and-performance-optimization",children:"Indexing and Performance Optimization"}),"\n",(0,i.jsxs)(s.p,{children:["By ",(0,i.jsx)(s.a,{href:"/rx-schema.html",children:"defining indexes"})," on frequently searched fields, you can speed up queries and reduce overall resource usage. This is crucial for larger datasets where performance might otherwise degrade."]}),"\n",(0,i.jsx)(s.h3,{id:"json-key-compression",children:"JSON Key Compression"}),"\n",(0,i.jsxs)(s.p,{children:["This ",(0,i.jsx)(s.a,{href:"/key-compression.html",children:"optimization"})," shortens field names in stored JSON documents, thereby reducing storage space and potentially improving performance for read/write operations."]}),"\n",(0,i.jsx)(s.h3,{id:"multi-tab-support",children:"Multi-Tab Support"}),"\n",(0,i.jsx)(s.p,{children:"If your users open multiple tabs of your Vue application, RxDB ensures data is synchronized across all instances in real time. Changes made in one tab are immediately reflected in others, creating a unified user experience."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/multiwindow.gif",alt:"multi tab support",width:"450"})}),"\n",(0,i.jsx)(s.h2,{id:"best-practices-for-using-rxdb-in-vue",children:"Best Practices for Using RxDB in Vue"}),"\n",(0,i.jsx)(s.p,{children:"Here are some recommendations to get the most out of RxDB in your Vue projects:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"Centralize Database Creation: Initialize and configure RxDB in a dedicated file or plugin, ensuring only one database instance is created."}),"\n",(0,i.jsx)(s.li,{children:"Leverage Vue's Composition API or a Global Store: Use watchers, refs, or a store like Pinia to neatly manage data subscriptions and updates, preventing scattered subscription logic."}),"\n",(0,i.jsx)(s.li,{children:"Async Subscriptions: Prefer using Vue's lifecycle hooks and the Composition API to manage subscriptions. Clean up subscriptions when components unmount or no longer need the data."}),"\n",(0,i.jsx)(s.li,{children:"Optimize Queries and Indexes: Only query the data you need, and define indexes to speed up lookups."}),"\n",(0,i.jsxs)(s.li,{children:["Test ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"Offline Scenarios"}),": Make sure your offline logic works as expected by simulating network disconnections and reconnections."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"Plan Conflict Resolution"}),": For multi-user apps, decide how to merge concurrent changes to prevent data inconsistencies."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsx)(s.p,{children:"To explore more about RxDB and leverage its capabilities for browser database development, check out the following resources:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/code/",children:"RxDB GitHub Repository"}),": Visit the official GitHub repository of RxDB to access the source code, documentation, and community support."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"}),": Get started quickly with RxDB by following the provided quickstart guide, which offers step-by-step instructions for setting up and using RxDB in your projects."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/reactivity.html",children:"RxDB Reactivity for Vue"}),": Discover how RxDB observables can directly produce Vue refs, simplifying integration with your Vue components."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/vue",children:"RxDB Vue Example at GitHub"}),": Explore an official Vue example to see RxDB in action within a Vue application."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples",children:"RxDB Examples"}),": Browse even more official examples to learn best practices you can apply to your own projects."]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,a.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>o,x:()=>t});var r=n(6540);const i={},a=r.createContext(i);function o(e){const s=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function t(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),r.createElement(a.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/b30f4f1f.0b7faa87.js b/docs/assets/js/b30f4f1f.0b7faa87.js
deleted file mode 100644
index 5b9d2d5da8a..00000000000
--- a/docs/assets/js/b30f4f1f.0b7faa87.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[3779],{8305(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>o,default:()=>d,frontMatter:()=>i,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"rx-attachment","title":"Attachments","description":"Learn how to store and manage binary data like images or files in RxDB using attachments. Discover features, performance benefits, encryption, compression, and usage examples with code.","source":"@site/docs/rx-attachment.md","sourceDirName":".","slug":"/rx-attachment.html","permalink":"/rx-attachment.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Attachments","slug":"rx-attachment.html","description":"Learn how to store and manage binary data like images or files in RxDB using attachments. Discover features, performance benefits, encryption, compression, and usage examples with code."},"sidebar":"tutorialSidebar","previous":{"title":"Storage Migration","permalink":"/migration-storage.html"},"next":{"title":"RxPipelines","permalink":"/rx-pipeline.html"}}');var a=n(4848),t=n(8453);const i={title:"Attachments",slug:"rx-attachment.html",description:"Learn how to store and manage binary data like images or files in RxDB using attachments. Discover features, performance benefits, encryption, compression, and usage examples with code."},o="Attachments",l={},c=[{value:"Add the attachments plugin",id:"add-the-attachments-plugin",level:2},{value:"Enable attachments in the schema",id:"enable-attachments-in-the-schema",level:2},{value:"putAttachment()",id:"putattachment",level:2},{value:"putAttachmentBase64()",id:"putattachmentbase64",level:2},{value:"getAttachment()",id:"getattachment",level:2},{value:"allAttachments()",id:"allattachments",level:2},{value:"allAttachments$",id:"allattachments-1",level:2},{value:"RxAttachment",id:"rxattachment",level:2},{value:"doc",id:"doc",level:3},{value:"id",id:"id",level:3},{value:"type",id:"type",level:3},{value:"length",id:"length",level:3},{value:"digest",id:"digest",level:3},{value:"rev",id:"rev",level:3},{value:"remove()",id:"remove",level:3},{value:"getData()",id:"getdata",level:2},{value:"getDataBase64()",id:"getdatabase64",level:2},{value:"getStringData()",id:"getstringdata",level:2}];function h(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.header,{children:(0,a.jsx)(s.h1,{id:"attachments",children:"Attachments"})}),"\n",(0,a.jsxs)(s.p,{children:["Attachments are binary data files that can be attachment to an ",(0,a.jsx)(s.code,{children:"RxDocument"}),", like a file that is attached to an email."]}),"\n",(0,a.jsxs)(s.p,{children:["Using attachments instead of adding the data to the normal document, ensures that you still have a good ",(0,a.jsx)(s.strong,{children:"performance"})," when querying and writing documents, even when a big amount of data, like an image file has to be stored."]}),"\n",(0,a.jsxs)(s.ul,{children:["\n",(0,a.jsx)(s.li,{children:"You can store string, binary files, images and whatever you want side by side with your documents."}),"\n",(0,a.jsx)(s.li,{children:"Deleted documents automatically loose all their attachments data."}),"\n",(0,a.jsx)(s.li,{children:"Not all replication plugins support the replication of attachments."}),"\n",(0,a.jsxs)(s.li,{children:["Attachments can be stored ",(0,a.jsx)(s.a,{href:"/encryption.html",children:"encrypted"}),"."]}),"\n"]}),"\n",(0,a.jsxs)(s.p,{children:["Internally, attachments in RxDB are stored and handled similar to how ",(0,a.jsx)(s.a,{href:"https://pouchdb.com/guides/attachments.html#how-attachments-are-stored",children:"CouchDB, PouchDB"})," does it."]}),"\n",(0,a.jsx)(s.h2,{id:"add-the-attachments-plugin",children:"Add the attachments plugin"}),"\n",(0,a.jsxs)(s.p,{children:["To enable the attachments, you have to add the ",(0,a.jsx)(s.code,{children:"attachments"})," plugin."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { addRxPlugin } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { RxDBAttachmentsPlugin } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/attachments'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"addRxPlugin"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(RxDBAttachmentsPlugin);"})]})]})})}),"\n",(0,a.jsx)(s.h2,{id:"enable-attachments-in-the-schema",children:"Enable attachments in the schema"}),"\n",(0,a.jsxs)(s.p,{children:["Before you can use attachments, you have to ensure that the attachments-object is set in the schema of your ",(0,a.jsx)(s.code,{children:"RxCollection"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mySchema"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ."})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ."})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encrypted"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // if true, the attachment-data will be encrypted with the db-password"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myCollection"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" humans"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" mySchema"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"putattachment",children:"putAttachment()"}),"\n",(0,a.jsxs)(s.p,{children:["Adds an attachment to a ",(0,a.jsx)(s.code,{children:"RxDocument"}),". Returns a Promise with the new attachment."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createBlob } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".putAttachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'cat.txt'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // (string) name of the attachment"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" data"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createBlob"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'meowmeow'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/plain'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // (string|Blob) data of the attachment"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/plain'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // (string) type of the attachment-data like 'image/jpeg'"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,a.jsx)(s.admonition,{type:"warning",children:(0,a.jsxs)(s.p,{children:["Expo/React-Native does not support the ",(0,a.jsx)(s.code,{children:"Blob"})," API natively. Make sure you use your own polyfill that properly supports ",(0,a.jsx)(s.code,{children:"blob.arrayBuffer()"})," when using RxAttachments or use the ",(0,a.jsx)(s.code,{children:"putAttachmentBase64()"})," and ",(0,a.jsx)(s.code,{children:"getDataBase64()"})," so that you do not have to create blobs."]})}),"\n",(0,a.jsx)(s.h2,{id:"putattachmentbase64",children:"putAttachmentBase64()"}),"\n",(0,a.jsxs)(s.p,{children:["Same as ",(0,a.jsx)(s.code,{children:"putAttachment()"})," but accepts a plain base64 string instead of a ",(0,a.jsx)(s.code,{children:"Blob"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".putAttachmentBase64"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'cat.txt'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" length"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 4"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" data"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'bWVvdw=='"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text/plain'"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"getattachment",children:"getAttachment()"}),"\n",(0,a.jsxs)(s.p,{children:["Returns an ",(0,a.jsx)(s.code,{children:"RxAttachment"})," by its id. Returns ",(0,a.jsx)(s.code,{children:"null"})," when the attachment does not exist."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getAttachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'cat.jpg'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]})})})}),"\n",(0,a.jsx)(s.h2,{id:"allattachments",children:"allAttachments()"}),"\n",(0,a.jsxs)(s.p,{children:["Returns an array of all attachments of the ",(0,a.jsx)(s.code,{children:"RxDocument"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsx)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachments"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".allAttachments"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})})})}),"\n",(0,a.jsx)(s.h2,{id:"allattachments-1",children:"allAttachments$"}),"\n",(0,a.jsx)(s.p,{children:"Gets an Observable which emits a stream of all attachments from the document. Re-emits each time an attachment gets added or removed from the RxDocument."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" all"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [];"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"allAttachments$"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" attachments "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" all "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,a.jsx)(s.h2,{id:"rxattachment",children:"RxAttachment"}),"\n",(0,a.jsxs)(s.p,{children:["The attachments of RxDB are represented by the type ",(0,a.jsx)(s.code,{children:"RxAttachment"})," which has the following attributes/methods."]}),"\n",(0,a.jsx)(s.h3,{id:"doc",children:"doc"}),"\n",(0,a.jsxs)(s.p,{children:["The ",(0,a.jsx)(s.code,{children:"RxDocument"})," which the attachment is assigned to."]}),"\n",(0,a.jsx)(s.h3,{id:"id",children:"id"}),"\n",(0,a.jsxs)(s.p,{children:["The id as ",(0,a.jsx)(s.code,{children:"string"})," of the attachment."]}),"\n",(0,a.jsx)(s.h3,{id:"type",children:"type"}),"\n",(0,a.jsxs)(s.p,{children:["The type as ",(0,a.jsx)(s.code,{children:"string"})," of the attachment."]}),"\n",(0,a.jsx)(s.h3,{id:"length",children:"length"}),"\n",(0,a.jsxs)(s.p,{children:["The length of the data of the attachment as ",(0,a.jsx)(s.code,{children:"number"}),"."]}),"\n",(0,a.jsx)(s.h3,{id:"digest",children:"digest"}),"\n",(0,a.jsxs)(s.p,{children:["The hash of the attachments data as ",(0,a.jsx)(s.code,{children:"string"}),"."]}),"\n",(0,a.jsx)(s.admonition,{type:"note",children:(0,a.jsx)(s.p,{children:"The digest is NOT calculated by RxDB, instead it is calculated by the RxStorage. The only guarantee is that the digest will change when the attachments data changes."})}),"\n",(0,a.jsx)(s.h3,{id:"rev",children:"rev"}),"\n",(0,a.jsxs)(s.p,{children:["The revision-number of the attachment as ",(0,a.jsx)(s.code,{children:"number"}),"."]}),"\n",(0,a.jsx)(s.h3,{id:"remove",children:"remove()"}),"\n",(0,a.jsx)(s.p,{children:"Removes the attachment. Returns a Promise that resolves when done."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getAttachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'cat.jpg'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".remove"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]})]})})}),"\n",(0,a.jsx)(s.h2,{id:"getdata",children:"getData()"}),"\n",(0,a.jsxs)(s.p,{children:["Returns a Promise which resolves the attachment's data as ",(0,a.jsx)(s.code,{children:"Blob"}),". (async)"]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getAttachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'cat.jpg'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" blob"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getData"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// Blob"})]})]})})}),"\n",(0,a.jsx)(s.h2,{id:"getdatabase64",children:"getDataBase64()"}),"\n",(0,a.jsxs)(s.p,{children:["Returns a Promise which resolves the attachment's data as ",(0,a.jsx)(s.strong,{children:"base64"})," ",(0,a.jsx)(s.code,{children:"string"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getAttachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'cat.jpg'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" base64Database"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getDataBase64"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'bWVvdw=='"})]})]})})}),"\n",(0,a.jsx)(s.h2,{id:"getstringdata",children:"getStringData()"}),"\n",(0,a.jsxs)(s.p,{children:["Returns a Promise which resolves the attachment's data as ",(0,a.jsx)(s.code,{children:"string"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getAttachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'cat.jpg'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" data"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" attachment"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".getStringData"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(); "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// 'meow'"})]})]})})}),"\n",(0,a.jsx)(s.h1,{id:"attachment-compression",children:"Attachment compression"}),"\n",(0,a.jsxs)(s.p,{children:["Storing many attachments can be a problem when the disc space of the device is exceeded.\nTherefore it can make sense to compress the attachments before storing them in the ",(0,a.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"}),".\nWith the ",(0,a.jsx)(s.code,{children:"attachments-compression"})," plugin you can compress the attachments data on write and decompress it on reads.\nThis happens internally and will now change on how you use the api. The compression is run with the ",(0,a.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Compression_Streams_API",children:"Compression Streams API"})," which is only supported on ",(0,a.jsx)(s.a,{href:"https://caniuse.com/?search=compressionstream",children:"newer browsers"}),"."]}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" wrappedAttachmentsCompressionStorage"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/attachments-compression'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageIndexedDB } "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb-premium/plugins/storage-indexeddb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create a wrapped storage with attachment-compression."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" storageWithAttachmentsCompression"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedAttachmentsCompressionStorage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageIndexedDB"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storageWithAttachmentsCompression"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// set the compression mode at the schema level"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" mySchema"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ."})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ."})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // ."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" attachments"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" compression"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'deflate'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- Specify the compression mode here. OneOf ['deflate', 'gzip']"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* ... create your collections as usual and store attachments in them. */"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "})]})})})]})}function d(e={}){const{wrapper:s}={...(0,t.R)(),...e.components};return s?(0,a.jsx)(s,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}},8453(e,s,n){n.d(s,{R:()=>i,x:()=>o});var r=n(6540);const a={},t=r.createContext(a);function i(e){const s=r.useContext(t);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function o(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),r.createElement(t.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/b672caf7.019a15fb.js b/docs/assets/js/b672caf7.019a15fb.js
deleted file mode 100644
index e41351756eb..00000000000
--- a/docs/assets/js/b672caf7.019a15fb.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[813],{8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}},9264(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/javascript-vector-database","title":"Local JavaScript Vector Database that works offline","description":"Create a blazing-fast vector database in JavaScript. Leverage RxDB and transformers.js for instant, offline semantic search - no servers required!","source":"@site/docs/articles/javascript-vector-database.md","sourceDirName":"articles","slug":"/articles/javascript-vector-database.html","permalink":"/articles/javascript-vector-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Local JavaScript Vector Database that works offline","slug":"javascript-vector-database.html","description":"Create a blazing-fast vector database in JavaScript. Leverage RxDB and transformers.js for instant, offline semantic search - no servers required!"},"sidebar":"tutorialSidebar","previous":{"title":"Fulltext Search \ud83d\udc51","permalink":"/fulltext-search.html"},"next":{"title":"Query Optimizer \ud83d\udc51","permalink":"/query-optimizer.html"}}');var i=n(4848),o=n(8453);const a={title:"Local JavaScript Vector Database that works offline",slug:"javascript-vector-database.html",description:"Create a blazing-fast vector database in JavaScript. Leverage RxDB and transformers.js for instant, offline semantic search - no servers required!"},l="Local Vector Database with RxDB and transformers.js in JavaScript",t={},c=[{value:"What is a Vector Database?",id:"what-is-a-vector-database",level:2},{value:"Generating Embeddings Locally in a Browser",id:"generating-embeddings-locally-in-a-browser",level:2},{value:"Storing the Embeddings in RxDB",id:"storing-the-embeddings-in-rxdb",level:2},{value:"Comparing Vectors by calculating the distance",id:"comparing-vectors-by-calculating-the-distance",level:2},{value:"Searching the Vector database with a full table scan",id:"searching-the-vector-database-with-a-full-table-scan",level:2},{value:"Indexing the Embeddings for Better Performance",id:"indexing-the-embeddings-for-better-performance",level:2},{value:"Vector Indexing Methods",id:"vector-indexing-methods",level:3},{value:"Storing indexed embeddings in RxDB",id:"storing-indexed-embeddings-in-rxdb",level:3},{value:"Searching the Vector database with utilization of the indexes",id:"searching-the-vector-database-with-utilization-of-the-indexes",level:2},{value:"Performance benchmarks",id:"performance-benchmarks",level:2},{value:"Performance of the Query Methods",id:"performance-of-the-query-methods",level:3},{value:"Performance of the Models",id:"performance-of-the-models",level:3},{value:"Potential Performance Optimizations",id:"potential-performance-optimizations",level:2},{value:"Migrating Data on Model/Index Changes",id:"migrating-data-on-modelindex-changes",level:2},{value:"Possible Future Improvements to Local-First Vector Databases",id:"possible-future-improvements-to-local-first-vector-databases",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"local-vector-database-with-rxdb-and-transformersjs-in-javascript",children:"Local Vector Database with RxDB and transformers.js in JavaScript"})}),"\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"local-first"})," revolution is here, changing the way we build apps! Imagine a world where your app's data lives right on the user's device, always available, even when there's no internet. That's the magic of local-first apps. Not only do they bring faster performance and limitless scalability, but they also empower users to work offline without missing a beat. And leading the charge in this space are local database solutions, like ",(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"}),"."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsxs)(s.p,{children:["But here's where things get even more exciting: when building ",(0,i.jsx)(s.a,{href:"/articles/local-first-future.html",children:"local-first"})," apps, traditional databases often fall short. They're great at searching for exact matches, like ",(0,i.jsx)(s.code,{children:"numbers"})," or ",(0,i.jsx)(s.code,{children:"strings"}),", but what if you want to search by ",(0,i.jsx)(s.strong,{children:"meaning"}),", like sifting through emails to find a specific topic? Sure, you could use ",(0,i.jsx)(s.strong,{children:"RegExp"}),", but to truly unlock the power of semantic search and similarity-based queries, you need something more cutting-edge. Something that really understands the content of the data."]}),"\n",(0,i.jsxs)(s.p,{children:["Enter ",(0,i.jsx)(s.strong,{children:"vector databases"}),", the game-changers for searching data by meaning! They have unlocked these new possibilities for storing and querying data, especially in tasks requiring ",(0,i.jsx)(s.strong,{children:"semantic search"})," and ",(0,i.jsx)(s.strong,{children:"similarity-based"})," queries. With the help of a ",(0,i.jsx)(s.strong,{children:"machine learning model"}),", data is transformed into a vector representation that can be stored, queried and compared in a database."]}),"\n",(0,i.jsxs)(s.p,{children:["But unfortunately, most vector databases are designed for server-side use, typically running in large cloud clusters, not to run on a users device. To fix that, in this article, we will combine ",(0,i.jsx)(s.strong,{children:"RxDB"})," and ",(0,i.jsx)(s.strong,{children:"transformers.js"})," to create a local vector database running in the ",(0,i.jsx)(s.strong,{children:"browser"})," with ",(0,i.jsx)(s.strong,{children:"JavaScript"}),". It stores data in ",(0,i.jsx)(s.strong,{children:"IndexedDB"}),", and uses a machine learning model with ",(0,i.jsx)(s.strong,{children:"WebAssembly"})," locally, without the need for external servers."]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://github.com/xenova/transformers.js",children:"transformers.js"})," is a powerful framework that allows machine learning models to run directly within JavaScript using WebAssembly or WebGPU."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," is a NoSQL, local-first database with a flexible storage layer that can run on any JavaScript runtime, including browsers and mobile environments. (You are reading this article on the RxDB docs)."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:"A local vector database offers several key benefits:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Zero network latency"}),": Data is processed locally on the user's device, ensuring near-instant responses."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline functionality"}),": Data can be queried even without an internet connection."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Enhanced privacy"}),": Sensitive information remains on the device, never needing to leave for external processing."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Simple setup"}),": No backend servers are required, making deployment straightforward."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Cost savings"}),": By running everything locally, you avoid fees for API access or cloud services for large language models."]}),"\n"]}),"\n",(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["In this article only the important source code parts are shown. You can find the full open-source vector database implementation at the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/javascript-vector-database",children:"github repository"}),"."]})}),"\n",(0,i.jsx)(s.h2,{id:"what-is-a-vector-database",children:"What is a Vector Database?"}),"\n",(0,i.jsxs)(s.p,{children:["A vector database is a specialized database optimized for storing and querying data in the form of ",(0,i.jsx)(s.strong,{children:"high-dimensional"})," vectors, often referred to as ",(0,i.jsx)(s.strong,{children:"embeddings"}),". These embeddings are numerical representations of data, such as text, images, or audio, created by machine learning models like ",(0,i.jsx)(s.a,{href:"https://huggingface.co/Xenova/all-MiniLM-L6-v2",children:"MiniLM"}),". Unlike traditional databases that work with exact matches on predefined fields, vector databases focus on ",(0,i.jsx)(s.strong,{children:"semantic similarity"}),", allowing you to query data based on meaning rather than exact values."]}),"\n",(0,i.jsxs)(s.blockquote,{children:["\n",(0,i.jsxs)(s.p,{children:["A vector, or embedding, is essentially an array of numbers, like ",(0,i.jsx)(s.code,{children:"[0.56, 0.12, -0.34, -0.90]"}),"."]}),"\n"]}),"\n",(0,i.jsx)(s.p,{children:'For example, instead of asking "Which document has the word \'database\'?", you can query "Which documents discuss similar topics to this one?" The vector database compares embeddings and returns results based on how similar the vectors are to each other.'}),"\n",(0,i.jsxs)(s.p,{children:["Vector databases handle multiple types of data beyond ",(0,i.jsx)(s.strong,{children:"text"}),", including ",(0,i.jsx)(s.strong,{children:"images"}),", ",(0,i.jsx)(s.strong,{children:"videos"}),", and ",(0,i.jsx)(s.strong,{children:"audio"})," files, all transformed into embeddings for efficient querying. Mostly you would not train a model by yourself and instead use one of the public available ",(0,i.jsx)(s.a,{href:"https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js",children:"transformer models"}),"."]}),"\n",(0,i.jsx)(s.p,{children:"Vector databases are highly effective in various types of applications:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Similarity Search"}),": Finds the closest matches to a query, even when the query doesn't contain the exact terms."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Clustering"}),": Groups similar items based on the proximity of their vector representations."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Recommendations"}),": Suggests items based on shared characteristics."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Anomaly Detection"}),": Identifies outliers that differ from the norm."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Classification"}),": Assigns categories to data based on its vector's nearest neighbors."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["In this tutorial, we will build a vector database designed as a ",(0,i.jsx)(s.strong,{children:"Similarity Search"})," for ",(0,i.jsx)(s.strong,{children:"text"}),". For other use cases, the setup can be adapted accordingly. This flexibility is why ",(0,i.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," doesn't provide a dedicated vector-database plugin, but rather offers utility functions to help you build your own vector search system."]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("img",{src:"../files/icons/transformers.js.svg",alt:"transformers.js",width:"40"})}),"\n",(0,i.jsx)(s.h2,{id:"generating-embeddings-locally-in-a-browser",children:"Generating Embeddings Locally in a Browser"}),"\n",(0,i.jsxs)(s.p,{children:["For the first step to build a local-first vector database we need to compute embeddings directly on the user's device. This is where ",(0,i.jsx)(s.a,{href:"https://github.com/xenova/transformers.js",children:"transformers.js"})," from ",(0,i.jsx)(s.a,{href:"https://huggingface.co/docs/transformers.js/index",children:"huggingface"})," comes in, allowing us to run machine learning models in the browser with ",(0,i.jsx)(s.strong,{children:"WebAssembly"}),". Below is an implementation of a ",(0,i.jsx)(s.code,{children:"getEmbeddingFromText()"})," function, which takes a piece of text and transforms it into an embedding using the ",(0,i.jsx)(s.a,{href:"https://huggingface.co/Xenova/all-MiniLM-L6-v2",children:"Xenova/all-MiniLM-L6-v2"})," model:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"js","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"js","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { pipeline } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "@xenova/transformers"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pipePromise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" pipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'feature-extraction'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Xenova/all-MiniLM-L6-v2'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getEmbeddingFromText"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(text) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pipe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pipePromise;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" output"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" pipe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pooling"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "mean"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" normalize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"output"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".data);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"This function creates an embedding by running the text through a pre-trained model and returning it in the form of an array of numbers, which can then be stored and further processed locally."}),"\n",(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsx)(s.p,{children:"Vector embeddings from different machine learning models or versions are not compatible with each other. When you change your model, you have to recreate all embeddings for your data."})}),"\n",(0,i.jsx)(s.h2,{id:"storing-the-embeddings-in-rxdb",children:"Storing the Embeddings in RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["To store the embeddings, first we have to create our ",(0,i.jsx)(s.a,{href:"/rx-database.html",children:"RxDB Database"})," with the ",(0,i.jsx)(s.a,{href:"/rx-storage-localstorage.html",children:"localstorage storage"})," that stores data in the browsers ",(0,i.jsx)(s.a,{href:"/articles/localstorage.html",children:"localstorage"}),". For more advanced projects, you can use any other ",(0,i.jsx)(s.a,{href:"/rx-storage.html",children:"RxStorage"}),"."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'mydatabase'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Then we add a ",(0,i.jsx)(s.code,{children:"items"})," collection that stores our documents with the ",(0,i.jsx)(s.code,{children:"text"})," field that stores the content."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" items"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 20"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'text'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" itemsCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".items;"})]})]})})}),"\n",(0,i.jsxs)(s.p,{children:["In our ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/javascript-vector-database",children:"example repo"}),", we use the ",(0,i.jsx)(s.a,{href:"https://huggingface.co/datasets/Supabase/wikipedia-en-embeddings",children:"Wiki Embeddings"})," dataset from supabase which was transformed and used to fill up the ",(0,i.jsx)(s.code,{children:"items"})," collection with test data."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" imported"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" itemsCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".count"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" fetch"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'./files/items.json'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" items"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" response"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".json"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" insertResult"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" itemsCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".bulkInsert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" items"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Also we need a ",(0,i.jsx)(s.code,{children:"vector"})," collection that stores our embeddings.\nRxDB, as a NoSQL database, allows for the storage of flexible data structures, such as embeddings, within documents. To achieve this, we need to define a ",(0,i.jsx)(s.a,{href:"/rx-schema.html",children:"schema"})," that specifies how the embeddings will be stored alongside each document. The schema includes fields for an ",(0,i.jsx)(s.code,{children:"id"})," and the ",(0,i.jsx)(s.code,{children:"embedding"})," array itself."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" vector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 20"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'array'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" items"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'embedding'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".vector;"})]})]})})}),"\n",(0,i.jsx)(s.p,{children:"When storing documents in the database, we need to ensure that the embeddings for these documents are generated and stored automatically. This requires a handler that runs during every document write, calling the machine learning model to generate the embeddings and storing them in a separate vector collection."}),"\n",(0,i.jsxs)(s.p,{children:["Since our app runs in a browser, it's essential to avoid duplicate work when ",(0,i.jsx)(s.strong,{children:"multiple browser tabs"})," are open and ensure efficient use of resources. Furthermore, we want the app to resume processing documents from where it left off if it's closed or interrupted. To achieve this, RxDB provides a ",(0,i.jsx)(s.a,{href:"/rx-pipeline.html",children:"pipeline plugin"}),", which allows us to set up a workflow that processes items and stores their embeddings. In our example, a pipeline takes batches of 10 documents, generates embeddings, and stores them in a separate vector collection."]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" itemsCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-embeddings-pipeline'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".all"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"docs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getVectorFromText"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".text);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".upsert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".primary"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" embedding"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }));"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["However, processing data locally presents performance challenges. Running the handler with a batch size of 10 takes around ",(0,i.jsx)(s.strong,{children:"2-4 seconds per batch"}),", meaning processing 10k documents would take up to an hour. To improve performance, we can do parallel processing using ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers",children:"WebWorkers"}),". A WebWorker runs on a different JavaScript process and we can start and run many of them in parallel."]}),"\n",(0,i.jsx)(s.p,{children:"Our worker listens for messages and performance the embedding generation on each request. It then sends the result embedding back to the main thread."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// worker.js"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getVectorFromText } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" './vector.js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"onmessage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (e) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getVectorFromText"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"e"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"data"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".text);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" postMessage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" e"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"data"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" embedding"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"On the main thread we spawn one worker per core and send the tasks to the worker instead of processing them on the main thread."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create one WebWorker per core"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" workers"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"navigator"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".hardwareConcurrency)"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .fill"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Worker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" URL"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"worker.js"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"meta"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".url)));"})]})]})})}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastWorkerId "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastId "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"export"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getVectorFromTextWithWorker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(text"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" string"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"number"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"[]> {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" worker "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workers[lastWorkerId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"++"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"];"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"!"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"worker) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" lastWorkerId "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" worker "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" workers[lastWorkerId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"++"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"];"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (lastId"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"++"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" ''"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"number"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"[]>(res "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" listener"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (ev"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:") "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" if"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"ev"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"data"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".id "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"==="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" res"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"ev"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"data"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".embedding);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" worker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".removeEventListener"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'message'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" listener);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" worker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addEventListener"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'message'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" listener);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" worker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".postMessage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" text"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" itemsCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" identifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'my-embeddings-pipeline'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" destination"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" navigator"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".hardwareConcurrency"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // one per CPU core"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".all"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"docs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getVectorFromTextWithWorker"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".body);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["This setup allows us to utilize the full hardware capacity of the client's machine. By setting the batch size to match the number of logical processors available (using the ",(0,i.jsx)(s.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency",children:"navigator.hardwareConcurrency"})," API) and running one worker per processor, we can reduce the processing time for 10k embeddings to ",(0,i.jsx)(s.strong,{children:"about 5 minutes"})," on my developer laptop with 32 CPU cores."]}),"\n",(0,i.jsx)(s.h2,{id:"comparing-vectors-by-calculating-the-distance",children:"Comparing Vectors by calculating the distance"}),"\n",(0,i.jsxs)(s.p,{children:["Now that we have stored our embeddings in the database, the next step is to compare these vectors to each other. Various methods are available to measure the similarity or difference between two vectors, such as ",(0,i.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Euclidean_distance",children:"Euclidean distance"}),", ",(0,i.jsx)(s.a,{href:"https://www.singlestore.com/blog/distance-metrics-in-machine-learning-simplfied/",children:"Manhattan distance"}),", ",(0,i.jsx)(s.a,{href:"https://tomhazledine.com/cosine-similarity/",children:"Cosine similarity"}),", and ",(0,i.jsx)(s.strong,{children:"Jaccard similarity"})," (and more). RxDB provides utility functions for each of these methods, making it easy to choose the most suitable method for your application. In this tutorial, we will use ",(0,i.jsx)(s.strong,{children:"Euclidean distance"})," to compare vectors. However, the ideal algorithm may vary depending on your data's distribution and the specific type of query you are performing. To find the optimal method for your app, it is up to you to try out all of these and compare the results."]}),"\n",(0,i.jsx)(s.p,{children:"Each method gets two vectors as input and returns a single number. Here's how to calculate the Euclidean distance between two embeddings with the vector utilities from RxDB:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { euclideanDistance } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/vector'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" distance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(embedding1"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" embedding2);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(distance); "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// 25.20443"})]})]})})}),"\n",(0,i.jsx)(s.p,{children:"With this we can sort multiple embeddings by how good they match our search query vector."}),"\n",(0,i.jsx)(s.h2,{id:"searching-the-vector-database-with-a-full-table-scan",children:"Searching the Vector database with a full table scan"}),"\n",(0,i.jsx)(s.p,{children:"To find out if our embeddings have been stored correctly and that our vector comparison works as should, let's run a basic query to ensure everything functions as expected. In this query, we aim to find documents similar to a given user input text. The process involves calculating the embedding from the input text, fetching all documents, calculating the distance between their embeddings and the query embedding, and then sorting them based on their similarity."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { euclideanDistance } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/vector'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { sortByObjectNumberProperty } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" userInput"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'new york people'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" queryVector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getEmbeddingFromText"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(userInput);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" candidates"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" withDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" candidates"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ({ "})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" distance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(queryVector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".embedding)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}));"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" queryResult"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" withDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"sortByObjectNumberProperty"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'distance'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"))"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".reverse"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(queryResult);"})]})]})})}),"\n",(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["For ",(0,i.jsx)(s.strong,{children:"distance"}),"-based comparisons, sorting should be in ascending order (smallest first), while for ",(0,i.jsx)(s.strong,{children:"similarity"}),"-based algorithms, the sorting should be in descending order (largest first)."]})}),"\n",(0,i.jsx)(s.p,{children:"If we inspect the results, we can see that the documents returned are ordered by relevance, with the most similar document at the top:"}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("img",{src:"../files/vector-database-result.png",alt:"Vector Database Result"})}),"\n",(0,i.jsx)(s.admonition,{type:"note",children:(0,i.jsxs)(s.p,{children:["This demo page can be ",(0,i.jsx)(s.a,{href:"https://pubkey.github.io/javascript-vector-database/",children:"run online here"}),"."]})}),"\n",(0,i.jsxs)(s.p,{children:["However our full-scan method presents a significant challenge: it does not scale well. As the number of stored documents increases, the time taken to fetch and compare embeddings grows proportionally. For example, retrieving embeddings from our ",(0,i.jsx)(s.a,{href:"https://huggingface.co/datasets/Supabase/wikipedia-en-embeddings",children:"test dataset"})," of 10k documents takes around ",(0,i.jsx)(s.strong,{children:"700 milliseconds"}),". If we scale up to 100k documents, this delay would rise to approximately ",(0,i.jsx)(s.strong,{children:"7 seconds"}),", making the search process inefficient for larger datasets."]}),"\n",(0,i.jsx)(s.h2,{id:"indexing-the-embeddings-for-better-performance",children:"Indexing the Embeddings for Better Performance"}),"\n",(0,i.jsxs)(s.p,{children:["To address the scalability issue, we need to store embeddings in a way that allows us to avoid fetching all of them from storage during a query. In traditional databases, you can sort documents by an ",(0,i.jsx)(s.strong,{children:"index field"}),", allowing efficient queries that retrieve only the necessary documents. An index organizes data in a structured, sortable manner, much ",(0,i.jsx)(s.strong,{children:"like a phone book"}),". However, with vector embeddings we are not dealing with simple, single values. Instead, we have large ",(0,i.jsx)(s.strong,{children:"lists of numbers"}),", which makes indexing more complex because we have more than one dimension."]}),"\n",(0,i.jsx)(s.h3,{id:"vector-indexing-methods",children:"Vector Indexing Methods"}),"\n",(0,i.jsx)(s.p,{children:"Various methods exist for indexing these vectors to improve query efficiency and performance:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"https://www.youtube.com/watch?v=Arni-zkqMBA",children:"Locality Sensitive Hashing (LSH)"}),": LSH hashes data so that similar items are likely to fall into the same bucket, optimizing approximate nearest neighbor searches in high-dimensional spaces by reducing the number of comparisons."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"https://www.youtube.com/watch?v=77QH0Y2PYKg",children:"Hierarchical Small World"}),": HSW is a graph structure designed for efficient navigation, allowing quick jumps across the graph while maintaining short paths between nodes, forming the basis for HNSW's optimization."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"https://www.youtube.com/watch?v=77QH0Y2PYKg",children:"Hierarchical Navigable Small Worlds (HNSW)"}),": HNSW builds a hierarchical graph for fast approximate nearest neighbor search. It uses multiple layers where higher layers represent fewer, more connected nodes, improving search efficiency in large datasets\u200b."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Distance to samples"}),": While testing different indexing strategies, ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey",children:"I"})," found out that using the distance to a sample set of items is a good way to index embeddings. You pick like 5 random items of your data and get the embeddings for them out of the model. These are your 5 index vectors. For each embedding stored in the vector database, we calculate the distance to our 5 index vectors and store that ",(0,i.jsx)(s.code,{children:"number"}),' as an index value. This seems to work good because similar things have similar distances to other things. For example the words "shoe" and "socks" have a similar distance to "boat" and therefore should have roughly the same index value.']}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["When building ",(0,i.jsx)(s.strong,{children:"local-first"})," applications, performance is often a challenge, especially in JavaScript. With ",(0,i.jsx)(s.strong,{children:"IndexedDB"}),", certain operations, like many sequential ",(0,i.jsx)(s.code,{children:"get by id"})," calls, ",(0,i.jsx)(s.a,{href:"/slow-indexeddb.html",children:"are slow"}),", while bulk operations, such as ",(0,i.jsx)(s.code,{children:"get by index range"}),", are fast. Therefore, it's essential to use an indexing method that allows embeddings to be stored in a sortable way, like ",(0,i.jsx)(s.strong,{children:"Locality Sensitive Hashing"})," or ",(0,i.jsx)(s.strong,{children:"Distance to Samples"}),". In this article, we'll use ",(0,i.jsx)(s.strong,{children:"Distance to Samples"}),", because for ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey",children:"me"})," it provides the best default behavior for the sample dataset."]}),"\n",(0,i.jsx)(s.h3,{id:"storing-indexed-embeddings-in-rxdb",children:"Storing indexed embeddings in RxDB"}),"\n",(0,i.jsxs)(s.p,{children:["The optimal way to store index values alongside embeddings in RxDB is to place them within the same ",(0,i.jsx)(s.a,{href:"/rx-collection.html",children:"RxCollection"}),". To ensure that the index values are both sortable and precise, we convert them into strings with a fixed length of ",(0,i.jsx)(s.code,{children:"10"})," characters. This standardization helps in managing values with many decimals and ensures proper sorting in the database."]}),"\n",(0,i.jsx)(s.p,{children:"Here's is our schema example schema where each document contains an embedding and corresponding index fields:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" indexSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "version"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "primaryKey"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "id"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "object"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "properties"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "id"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "string"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "maxLength"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "embedding"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "array"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "items"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "type"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "number"'})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // index fields"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx0"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx1"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx2"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx3"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexSchema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx4"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexSchema"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "required"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "id"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "embedding"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx0"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx1"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx2"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx3"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx4"'})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "indexes"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx0"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx1"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx2"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx3"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "idx4"'})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["To populate these index fields, we modify the ",(0,i.jsx)(s.a,{href:"/rx-pipeline.html",children:"RxPipeline"})," handler accordingly to the ",(0,i.jsx)(s.strong,{children:"Distance to samples"})," method. We calculate the distance between the document's embedding and our set of ",(0,i.jsx)(s.code,{children:"5"})," index vectors. The calculated distances are converted to ",(0,i.jsx)(s.code,{children:"string"})," and stored in the appropriate index fields:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { euclideanDistance } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/vector'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sampleVectors"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" number"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"[][] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"/* the index vectors */"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"];"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" itemsCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addPipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".all"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"docs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getEmbedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".text);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docData"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" doc"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".primary"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" embedding };"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // calculate the distance to all samples and store them in the index fields"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"5"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".fill"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((_"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" idx) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" indexValue"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sampleVectors[idx]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" embedding);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docData["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" idx] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" indexNrToString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(indexValue);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".upsert"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(docData);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }));"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"searching-the-vector-database-with-utilization-of-the-indexes",children:"Searching the Vector database with utilization of the indexes"}),"\n",(0,i.jsxs)(s.p,{children:["Once our embeddings are stored in an indexed format, we can perform searches much ",(0,i.jsx)(s.strong,{children:"more efficiently"})," than through a full table scan. While this indexing method boosts performance, it comes with a tradeoff: a slight loss in precision, meaning that the result set may not always be the optimal one. However, this is generally acceptable for ",(0,i.jsx)(s.strong,{children:"similarity search"})," use cases."]}),"\n",(0,i.jsx)(s.p,{children:"There are multiple ways to leverage indexes for faster queries. Here are two effective methods:"}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Query for Index Similarity in Both Directions"}),": For each index vector, calculate the distance to the search embedding and fetch all relevant embeddings in both directions (sorted before and after) from that value."]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" vectorSearchIndexSimilarity"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(searchEmbedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" number"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"[]) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsPerIndexSide"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" candidates"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Set"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RxDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".all"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"5"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".fill"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (_"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" distanceToIndex"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sampleVectors[i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" searchEmbedding);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"docsBefore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsAfter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".all"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(["})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $lt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" indexNrToString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(distanceToIndex)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'desc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" limit"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docsPerIndexSide"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" indexNrToString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(distanceToIndex)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" limit"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docsPerIndexSide"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ]);"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsBefore"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(d "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" candidates"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".add"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(d));"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsAfter"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(d "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" candidates"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".add"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(d));"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsWithDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(candidates)"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" distance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((doc "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:").embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" searchEmbedding);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" distance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" doc"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sorted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsWithDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"sortByObjectNumberProperty"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'distance'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"))"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".reverse"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" result"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sorted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".slice"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docReads"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(s.ol,{start:"2",children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Query for an Index Range with a Defined Distance"}),": Set an ",(0,i.jsx)(s.code,{children:"indexDistance"})," and retrieve all embeddings within a specified range from the index vector to the search embedding."]}),"\n"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" vectorSearchIndexRange"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(searchEmbedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" number"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"[]) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" pipeline"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".awaitIdle"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" indexDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0.003"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" candidates"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Set"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"RxDocument"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" let"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docReads "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Promise"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".all"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"5"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".fill"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (_"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" distanceToIndex"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(sampleVectors[i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" searchEmbedding);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" range"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" distanceToIndex "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"*"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexDistance;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" vectorCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" indexNrToString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(distanceToIndex "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"-"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" range)"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" $lt"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" indexNrToString"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(distanceToIndex "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" range)"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [{ ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" i]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'asc'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".exec"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(d "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" candidates"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".add"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(d));"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docReads "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docReads "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"+"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"."}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"length"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" );"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsWithDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(candidates)"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(doc "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" distance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((doc "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"as"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" any"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:").embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" searchEmbedding);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" distance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" doc"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sorted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" docsWithDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".sort"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"sortByObjectNumberProperty"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'distance'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"))"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".reverse"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" result"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" sorted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".slice"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 10"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docReads"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" };"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Both methods allow you to limit the number of embeddings fetched from storage while still ensuring a reasonably precise search result. However, they differ in how many embeddings are read and how precise the results are, with trade-offs between performance and accuracy. The first method has a known embedding read amount of ",(0,i.jsx)(s.code,{children:"docsPerIndexSide * 2 * [amount of indexes]"}),". The second method reads out an unknown amount of embeddings, depending on the sparsity of the dataset and the value of ",(0,i.jsx)(s.code,{children:"indexDistance"}),"."]}),"\n",(0,i.jsx)(s.p,{children:"And that's it for the implementation. We now have a local first vector database that is able to store and query vector data."}),"\n",(0,i.jsx)(s.h2,{id:"performance-benchmarks",children:"Performance benchmarks"}),"\n",(0,i.jsxs)(s.p,{children:["In server-side databases, performance can be improved by scaling hardware or adding more servers. However, ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"local-first"})," apps face the unique challenge that the hardware is determined by the end user, making performance unpredictable. Some users may have ",(0,i.jsx)(s.strong,{children:"high-end gaming PCs"}),", while others might be using ",(0,i.jsx)(s.strong,{children:"outdated smartphones in power-saving mode"}),". Therefore, when building a local-first app that processes more than a few documents, performance becomes a critical factor and should be thoroughly tested upfront."]}),"\n",(0,i.jsxs)(s.p,{children:["Let's run performance benchmarks on my ",(0,i.jsx)(s.strong,{children:"high-end gaming PC"})," to give you a sense of how long different operations take and what's achievable."]}),"\n",(0,i.jsx)(s.h3,{id:"performance-of-the-query-methods",children:"Performance of the Query Methods"}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:"Query Method"}),(0,i.jsx)(s.th,{children:"Time in milliseconds"}),(0,i.jsx)(s.th,{children:"Docs read from storage"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Full Scan"}),(0,i.jsx)(s.td,{children:"765"}),(0,i.jsx)(s.td,{children:"10000"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Index Similarity"}),(0,i.jsx)(s.td,{children:"1647"}),(0,i.jsx)(s.td,{children:"934"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Index Range"}),(0,i.jsx)(s.td,{children:"88"}),(0,i.jsx)(s.td,{children:"2187"})]})]})]}),"\n",(0,i.jsxs)(s.p,{children:["As shown, the ",(0,i.jsx)(s.strong,{children:"index similarity"})," query method takes significantly longer compared to others. This is due to the need for descending sort orders in some queries ",(0,i.jsx)(s.code,{children:"sort: [{ ['idx' + i]: 'desc' }]"}),". While RxDB supports descending sorts, performance suffers because IndexedDB does not efficiently handle ",(0,i.jsx)(s.a,{href:"https://github.com/w3c/IndexedDB/issues/130",children:"reverse indexed bulk operations"}),". As a result, the ",(0,i.jsx)(s.strong,{children:"index range method"})," performs much better for this use case and should be used instead. With its query time of only ",(0,i.jsx)(s.code,{children:"88"})," milliseconds it is fast enough for all most things and likely such fast that you do not even need to show a loading spinner. Also it is faster compared to fetching the query result from a server-side vector database over the internet."]}),"\n",(0,i.jsx)(s.h3,{id:"performance-of-the-models",children:"Performance of the Models"}),"\n",(0,i.jsxs)(s.p,{children:["Let's also look at the time taken to calculate a single embedding across various models from the ",(0,i.jsx)(s.a,{href:"https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js",children:"huggingface transformers list"}),":"]}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:"Model Name"}),(0,i.jsx)(s.th,{children:"Time per Embedding in (ms)"}),(0,i.jsx)(s.th,{children:"Vector Size"}),(0,i.jsx)(s.th,{children:"Model Size (MB)"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Xenova/all-MiniLM-L6-v2"}),(0,i.jsx)(s.td,{children:"173"}),(0,i.jsx)(s.td,{children:"384"}),(0,i.jsx)(s.td,{children:"23"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Supabase/gte-small"}),(0,i.jsx)(s.td,{children:"341"}),(0,i.jsx)(s.td,{children:"384"}),(0,i.jsx)(s.td,{children:"34"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Xenova/paraphrase-multilingual-mpnet-base-v2"}),(0,i.jsx)(s.td,{children:"1000"}),(0,i.jsx)(s.td,{children:"768"}),(0,i.jsx)(s.td,{children:"279"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"jinaai/jina-embeddings-v2-base-de"}),(0,i.jsx)(s.td,{children:"1291"}),(0,i.jsx)(s.td,{children:"768"}),(0,i.jsx)(s.td,{children:"162"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"jinaai/jina-embeddings-v2-base-zh"}),(0,i.jsx)(s.td,{children:"1437"}),(0,i.jsx)(s.td,{children:"768"}),(0,i.jsx)(s.td,{children:"162"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"jinaai/jina-embeddings-v2-base-code"}),(0,i.jsx)(s.td,{children:"1769"}),(0,i.jsx)(s.td,{children:"768"}),(0,i.jsx)(s.td,{children:"162"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"mixedbread-ai/mxbai-embed-large-v1"}),(0,i.jsx)(s.td,{children:"3359"}),(0,i.jsx)(s.td,{children:"1024"}),(0,i.jsx)(s.td,{children:"337"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"WhereIsAI/UAE-Large-V1"}),(0,i.jsx)(s.td,{children:"3499"}),(0,i.jsx)(s.td,{children:"1024"}),(0,i.jsx)(s.td,{children:"337"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"Xenova/multilingual-e5-large"}),(0,i.jsx)(s.td,{children:"4215"}),(0,i.jsx)(s.td,{children:"1024"}),(0,i.jsx)(s.td,{children:"562"})]})]})]}),"\n",(0,i.jsxs)(s.p,{children:["From these benchmarks, it's evident that models with larger vector outputs ",(0,i.jsx)(s.strong,{children:"take longer to process"}),". Additionally, the model size significantly affects performance, with larger models requiring more time to compute embeddings. This trade-off between model complexity and performance must be considered when choosing the right model for your use case."]}),"\n",(0,i.jsx)(s.h2,{id:"potential-performance-optimizations",children:"Potential Performance Optimizations"}),"\n",(0,i.jsx)(s.p,{children:"There are multiple other techniques to improve the performance of your local vector database:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Shorten embeddings"}),': The storing and retrieval of embeddings can be improved by "shortening" the embedding. To do that, you just strip away numbers from your vector. For example ',(0,i.jsx)(s.code,{children:"[0.56, 0.12, -0.34, 0.78, -0.90]"})," becomes ",(0,i.jsx)(s.code,{children:"[0.56, 0.12]"}),'. That\'s it, you now have a smaller embedding that is faster to read out of the storage and calculating distances is faster because it has to process less numbers. The downside is that you loose precision in your search results. Sometimes shortening the embeddings makes more sense as a pre-query step where you first compare the shortened vectors and later fetch the "real" vectors for the 10 most matching documents to improve their sort order.']}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Optimize the variables in our Setup"}),": In this examples we picked our variables in a non-optimal way. You can get huge performance improvements by setting different values:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsx)(s.li,{children:"We picked 5 indexes for the embeddings. Using less indexes improves your query performance with the cost of less good results."}),"\n",(0,i.jsxs)(s.li,{children:["For queries that search by fetching a specific embedding distance we used the ",(0,i.jsx)(s.code,{children:"indexDistance"})," value of ",(0,i.jsx)(s.code,{children:"0.003"}),". Using a lower value means we read less document from the storage. This is faster but reduces the precision of the results which means we will get a less optimal result compared to a full table scan."]}),"\n",(0,i.jsxs)(s.li,{children:["For queries that search by fetching a given amount of documents per index side, we set the value ",(0,i.jsx)(s.code,{children:"docsPerIndexSide"})," to ",(0,i.jsx)(s.code,{children:"100"}),". Increasing this value means you fetch more data from the storage but also get a better precision in the search results. Decreasing it can improve query performance with worse precision."]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Use faster models"}),": There are many ways to improve performance of machine learning models. If your embedding calculation is too slow, try other models. ",(0,i.jsx)(s.strong,{children:"Smaller"})," mostly means ",(0,i.jsx)(s.strong,{children:"faster"}),". The model ",(0,i.jsx)(s.code,{children:"Xenova/all-MiniLM-L6-v2"})," which is used in this tutorial is about ",(0,i.jsx)(s.a,{href:"https://huggingface.co/Xenova/all-MiniLM-L6-v2/tree/main",children:"1 year old"}),". There exist better, more modern models to use. Huggingface makes these convenient to use. You only have to switch out the model name with any other model from ",(0,i.jsx)(s.a,{href:"https://huggingface.co/models?pipeline_tag=feature-extraction&library=transformers.js",children:"that site"}),"."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Narrow down the search space"}),': By utilizing other "normal" filter operators to your query, you can narrow down the search space and optimize performance. For example in an email search you could additionally use a operator that limits the results to all emails that are not older than one year.']}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Dimensionality Reduction"})," with an ",(0,i.jsx)(s.a,{href:"https://www.youtube.com/watch?v=D16rii8Azuw",children:"autoencoder"}),": An autoencoder encodes vector data with minimal loss which can improve the performance by having to store and compare less numbers in an embedding."]}),"\n"]}),"\n",(0,i.jsxs)(s.li,{children:["\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Different RxDB Plugins"}),": RxDB has different storages and plugins that can improve the performance like the ",(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"}),", the ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS RxStorage"}),", the ",(0,i.jsx)(s.a,{href:"/rx-storage-sharding.html",children:"sharding"})," plugin and the ",(0,i.jsx)(s.a,{href:"/rx-storage-worker.html",children:"Worker"})," and ",(0,i.jsx)(s.a,{href:"/rx-storage-shared-worker.html",children:"SharedWorker"})," storages."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"migrating-data-on-modelindex-changes",children:"Migrating Data on Model/Index Changes"}),"\n",(0,i.jsxs)(s.p,{children:["When you change the index parameter or even update the whole model which was used to create the embeddings, you have to migrate the data that is already stored on your users devices. RxDB offers the ",(0,i.jsx)(s.a,{href:"/migration-schema.html",children:"Schema Migration Plugin"})," for that."]}),"\n",(0,i.jsxs)(s.p,{children:["When the app is reloaded and the updated source code is started, RxDB detects changes in your ",(0,i.jsx)(s.a,{href:"/rx-schema.html#version",children:"schema version"})," and runs the ",(0,i.jsx)(s.a,{href:"/migration-schema.html#providing-strategies",children:"migration strategy"})," accordingly. So to update the stored data, increase the schema version and define a handler:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" schemaV1"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "version"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- increase schema version by 1"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "primaryKey"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "id"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "properties"'}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" /* ... */"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"};"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"In the migration handler we recreate the new embeddings and index values."}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" myDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" vectors"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schemaV1"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" migrationStrategies"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 1"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(docData){"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" embedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getEmbedding"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"docData"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".body);"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" new"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" Array"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"5"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".fill"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".map"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((_"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" idx) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docData["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'idx'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" +"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" idx] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" euclideanDistance"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(mySampleVectors[idx]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" embedding);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" docData;"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"possible-future-improvements-to-local-first-vector-databases",children:"Possible Future Improvements to Local-First Vector Databases"}),"\n",(0,i.jsx)(s.p,{children:"For now our vector database works and we are good to go. However there are some things to consider for the future:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"WebGPU"})," is ",(0,i.jsx)(s.a,{href:"https://caniuse.com/webgpu",children:"not fully supported"})," yet. When this changes, creating embeddings in the browser have the potential to become faster. You can check if your current chrome supports WebGPU by opening ",(0,i.jsx)(s.code,{children:"chrome://gpu/"}),". Notice that WebGPU has been reported to sometimes be ",(0,i.jsx)(s.a,{href:"https://github.com/xenova/transformers.js/issues/894#issuecomment-2323897485",children:"even slower"})," compared to WASM but likely it will be faster in the long term."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Cross-Modal AI Models"}),": While progress is being made, AI models that can understand and integrate multiple modalities are still in development. For example you could query for an ",(0,i.jsx)(s.strong,{children:"image"})," together with a ",(0,i.jsx)(s.strong,{children:"text"})," prompt to get a more detailed output."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Multi-Step queries"}),": In this article we only talked about having a single query as input and an ordered list of outputs. But there is big potential in chaining models or queries together where you take the results of one query and input them into a different model with different embeddings or outputs."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Shared/Like my ",(0,i.jsx)(s.a,{href:"https://x.com/rxdbjs/status/1833429569434427494",children:"announcement tweet"})]}),"\n",(0,i.jsxs)(s.li,{children:["Read the source code that belongs to this article ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/javascript-vector-database",children:"at github"})]}),"\n",(0,i.jsxs)(s.li,{children:["Learn how to use RxDB with the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"})]}),"\n",(0,i.jsxs)(s.li,{children:["Check out the ",(0,i.jsx)(s.a,{href:"https://github.com/pubkey/rxdb",children:"RxDB github repo"})," and leave a star \u2b50"]}),"\n"]})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/b8c49ce4.3962d1bb.js b/docs/assets/js/b8c49ce4.3962d1bb.js
deleted file mode 100644
index a0677b3c4a7..00000000000
--- a/docs/assets/js/b8c49ce4.3962d1bb.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[6355],{4971(e,n,t){t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>d,frontMatter:()=>o,metadata:()=>i,toc:()=>h});const i=JSON.parse('{"id":"releases/13.0.0","title":"RxDB 13.0.0 - A New Era of Replication","description":"Discover RxDB 13.0\'s brand-new replication protocol, faster performance, and real-time collaboration for a seamless offline-first experience.","source":"@site/docs/releases/13.0.0.md","sourceDirName":"releases","slug":"/releases/13.0.0.html","permalink":"/releases/13.0.0.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB 13.0.0 - A New Era of Replication","slug":"13.0.0.html","description":"Discover RxDB 13.0\'s brand-new replication protocol, faster performance, and real-time collaboration for a seamless offline-first experience."},"sidebar":"tutorialSidebar","previous":{"title":"14.0.0","permalink":"/releases/14.0.0.html"},"next":{"title":"12.0.0","permalink":"/releases/12.0.0.html"}}');var s=t(4848),r=t(8453);const o={title:"RxDB 13.0.0 - A New Era of Replication",slug:"13.0.0.html",description:"Discover RxDB 13.0's brand-new replication protocol, faster performance, and real-time collaboration for a seamless offline-first experience."},a="13.0.0",l={},h=[{value:"Other breaking changes",id:"other-breaking-changes",level:2},{value:"Other non breaking or internal changes",id:"other-non-breaking-or-internal-changes",level:2},{value:"New Features",id:"new-features",level:2},{value:"Migration to the new version",id:"migration-to-the-new-version",level:2}];function c(e){const n={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"1300",children:"13.0.0"})}),"\n",(0,s.jsxs)(n.p,{children:["So in the last major RxDB versions, the focus was set to ",(0,s.jsx)(n.strong,{children:"improvements of the storage engine"}),". This is done. RxDB has now ",(0,s.jsx)(n.a,{href:"/rx-storage.html",children:"multiple RxStorage implementations"}),", a better query planner and an improved test suite to ensure everything works correct.\nThis let to huge improvements in write and query performance and decreased the initial pageload of RxDB based applications."]}),"\n",(0,s.jsxs)(n.p,{children:["In the new major version ",(0,s.jsx)(n.code,{children:"13.0.0"}),", the focus was set to improvements to the ",(0,s.jsx)(n.strong,{children:"replication protocol"}),".\nWhen I first implemented the GraphQL replication a few years ago, I had a specific use case in mind and designed the whole protocol and replication plugins around that use case."]}),"\n",(0,s.jsx)(n.p,{children:"But the time has shown, that the old replication protocol is a big downside of RxDB:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["The replication relied on the backend to solve all ",(0,s.jsx)(n.strong,{children:"conflicts"}),". This was easy to implement into RxDB because the whole \tresponsibility was given away to the person that has to implement a compatible backend."]}),"\n",(0,s.jsxs)(n.li,{children:["In each point in time, the replication did either push or pull documents, but ",(0,s.jsx)(n.strong,{children:"never in parallel"}),". This slows done the whole replication process and makes RxDB not usable for the implementation of features like ",(0,s.jsx)(n.strong,{children:"multi-user-real-time-collaboration"})," or when many read- and write operations have to happen in a short timespan."]}),"\n",(0,s.jsxs)(n.li,{children:["After each ",(0,s.jsx)(n.code,{children:"push"}),", a ",(0,s.jsx)(n.code,{children:"pull"})," had to be run to check if the backend had changed the state to solve a conflict."]}),"\n",(0,s.jsx)(n.li,{children:"The replication protocol did not support attachments and was not designed to ever support them."}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["So in version ",(0,s.jsx)(n.code,{children:"13.0.0"})," I replaced the whole replication plugins with a new replication protocol. The main goals have been:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Push- and Pull in parallel."}),"\n",(0,s.jsx)(n.li,{children:"Use the data in the changestream (optional) to decrease replication latency."}),"\n",(0,s.jsxs)(n.li,{children:["Implement the conflict resolution into RxDB so that the ",(0,s.jsx)(n.strong,{children:"client resolves its own conflicts"})," and does not rely on the backend."]}),"\n",(0,s.jsxs)(n.li,{children:["Decrease the complexity for a compatible backend implementation. The new protocol relies on a ",(0,s.jsx)(n.em,{children:"dumb"})," backend. This will open compatibility with many other use cases like implementing ",(0,s.jsx)(n.a,{href:"https://github.com/supabase/supabase/discussions/357",children:"Offline-First in Supabase"})," or using CouchDB but having a faster replication compared to the native CouchDB replication."]}),"\n",(0,s.jsxs)(n.li,{children:["Make it possible to use ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type",children:"CRDTs"})," instead of a conflict resolution."]}),"\n",(0,s.jsx)(n.li,{children:"Design a the protocol in a way to make it possible to add attachments replication in the future."}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"On the RxDocument level, the replication works like git, where the fork/client contains all new writes and must be merged with the master/server before it can push its new state to the master/server."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"A---B1---C1---X master/server state\n \\ /\n B1---C2 fork/client state\n"})}),"\n",(0,s.jsxs)(n.p,{children:["For more details, read the ",(0,s.jsx)(n.a,{href:"/replication.html",children:"documentation about the new RxDB Sync Engine"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Backends that have been compatible with the previous RxDB versions ",(0,s.jsx)(n.code,{children:"12"})," and older, will not work with the new replication protocol. To learn how to do that, either read the ",(0,s.jsx)(n.a,{href:"/replication.html",children:"docs"})," or check out the ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/graphql",children:"GraphQL example"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"other-breaking-changes",children:"Other breaking changes"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["RENAMED the ",(0,s.jsx)(n.code,{children:"ajv-validate"})," plugin to ",(0,s.jsx)(n.code,{children:"validate-ajv"})," to be in equal with the other validation plugins."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"is-my-json-valid"})," validation is no longer supported until ",(0,s.jsx)(n.a,{href:"https://github.com/mafintosh/is-my-json-valid/pull/192",children:"this bug"})," is fixed."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REFACTORED the ",(0,s.jsx)(n.a,{href:"https://rxdb.info/schema-validation.html",children:"schema validation plugins"}),", they are no longer plugins but now they get wrapped around any other RxStorage."]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["It allows us to run the validation inside of a ",(0,s.jsx)(n.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage"})," instead of running it in the main JavaScript process."]}),"\n",(0,s.jsxs)(n.li,{children:["It allows us to configure which ",(0,s.jsx)(n.code,{children:"RxDatabase"})," instance must use the validation and which does not. In production it often makes sense to validate user data, but you might not need the validation for data that is only replicated from the backend."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REFACTORED the ",(0,s.jsx)(n.a,{href:"/key-compression.html",children:"key compression plugin"}),", it is no longer a plugin but now a wrapper around any other RxStorage."]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["It allows to run the key-compression inside of a ",(0,s.jsx)(n.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage"})," instead of running it in the main JavaScript process."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsx)(n.p,{children:"REFACTORED the encryption plugin, it is no longer a plugin but now a wrapper around any other RxStorage."}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["It allows to run the encryption inside of a ",(0,s.jsx)(n.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage"})," instead of running it in the main JavaScript process."]}),"\n",(0,s.jsxs)(n.li,{children:["It allows do use asynchronous crypto function like ",(0,s.jsx)(n.a,{href:"https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API",children:"WebCrypto"})]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsx)(n.p,{children:"Store the password hash in the same write request as the database token to improve performance."}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REMOVED support for temporary documents ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/pull/3777#issuecomment-1120669088",children:"see here"})]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REMOVED RxDatabase.broadcastChannel The broadcast channel has been moved out of the RxDatabase and is part of the RxStorage. So it is no longer exposed via ",(0,s.jsx)(n.code,{children:"RxDatabase.broadcastChannel"}),"."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["Removed the ",(0,s.jsx)(n.code,{children:"liveInterval"})," option of the replication plugins. It was an edge case feature with wrong defaults. If you want to run the pull replication on interval, you can send a ",(0,s.jsx)(n.code,{children:"RESYNC"})," event manually in a loop."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REPLACED ",(0,s.jsx)(n.code,{children:"RxReplicationPullError"})," and ",(0,s.jsx)(n.code,{children:"RxReplicationPushError"})," with normal ",(0,s.jsx)(n.code,{children:"RxError"})," like in the rest of the RxDB code."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REMOVED the option to filter out replication documents with the push/pull modifiers ",(0,s.jsx)(n.a,{href:"https://github.com/pubkey/rxdb/issues/2552",children:"#2552"})," because this does not work with the new replication protocol."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["CHANGE default of replication ",(0,s.jsx)(n.code,{children:"live"})," to be set to ",(0,s.jsx)(n.code,{children:"true"}),". Because most people want to do a live replication, not a one time replication."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["RENAMED the ",(0,s.jsx)(n.code,{children:"server"})," plugin is now called ",(0,s.jsx)(n.code,{children:"server-couchdb"})," and ",(0,s.jsx)(n.code,{children:"RxDatabase.server()"})," is now ",(0,s.jsx)(n.code,{children:"RxDatabase.serverCouchDB()"})]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["CHANGED Attachment data is now always handled as ",(0,s.jsx)(n.code,{children:"Blob"})," because Node.js does support ",(0,s.jsx)(n.code,{children:"Blob"})," since version 18.0.0 so we no longer have to use a ",(0,s.jsx)(n.code,{children:"Buffer"})," but instead can use Blob for browsers and Node.js"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REFACTORED the layout of ",(0,s.jsx)(n.code,{children:"RxChangeEvent"})," to better match the RxDB requirements and to fix the 'deleted-document-is-modified-but-still-deleted' bug."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["When used with Node.js, RxDB now requires Node.js version ",(0,s.jsx)(n.code,{children:"18.0.0"})," or higher."]}),"\n",(0,s.jsx)(n.h2,{id:"other-non-breaking-or-internal-changes",children:"Other non breaking or internal changes"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsx)(n.p,{children:"REMOVED many unused plugin hooks because they decreased the performance."}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["REMOVE RxStorageStatics ",(0,s.jsx)(n.code,{children:".hash"})," and ",(0,s.jsx)(n.code,{children:".hashKey"})]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["CHANGE removed default usage of ",(0,s.jsx)(n.code,{children:"md5"})," as default hashing. Use a faster non-cryptographic hash instead."]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["ADD option to pass a custom hash function when calling ",(0,s.jsx)(n.code,{children:"createRxDatabase"}),"."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["CHANGE use ",(0,s.jsx)(n.code,{children:"Float"})," instead of ",(0,s.jsx)(n.code,{children:"Int"})," to represent timestamps in GraphQL."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["FIXED multiple problems with encoding attachments data. We now use the ",(0,s.jsx)(n.code,{children:"js-base64"})," library which properly handles utf-8/binary/ascii transformations."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["In the RxDB internal ",(0,s.jsx)(n.code,{children:"_meta.lwt"})," field, we now use 2 decimals number of the unix timestamp in milliseconds."]}),"\n"]}),"\n",(0,s.jsxs)(n.li,{children:["\n",(0,s.jsxs)(n.p,{children:["ADDED ",(0,s.jsx)(n.code,{children:"checkpointSchema"})," to the ",(0,s.jsx)(n.code,{children:"RxStorage.statics"})," interface."]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"new-features",children:"New Features"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["ADDED the ",(0,s.jsx)(n.a,{href:"/replication-websocket.html",children:"websocket replication plugin"})]}),"\n",(0,s.jsxs)(n.li,{children:["ADDED the ",(0,s.jsx)(n.a,{href:"/rx-storage-foundationdb.html",children:"FoundationDB RxStorage"})]}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"migration-to-the-new-version",children:"Migration to the new version"}),"\n",(0,s.jsxs)(n.p,{children:["Stored data of the previous RxDB versions is not compatible with RxDB ",(0,s.jsx)(n.code,{children:"13.0.0"}),". So if you want to keep that data, you have to migrate it in a way. For most use cases you might want to just drop the data from the client and re-sync it again from the backend. To keep the data locally, you might want to use the ",(0,s.jsx)(n.a,{href:"/migration-storage.html",children:"storage migration plugin"}),"."]})]})}function d(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(c,{...e})}):c(e)}},8453(e,n,t){t.d(n,{R:()=>o,x:()=>a});var i=t(6540);const s={},r=i.createContext(s);function o(e){const n=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/badcd764.922f3eee.js b/docs/assets/js/badcd764.922f3eee.js
deleted file mode 100644
index ef9f22aa79d..00000000000
--- a/docs/assets/js/badcd764.922f3eee.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[8318],{3922(e,s,n){n.r(s),n.d(s,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/flutter-database","title":"Supercharge Flutter Apps with the RxDB Database","description":"Harness RxDB\'s reactive database to bring real-time, offline-first data storage and syncing to your next Flutter application.","source":"@site/docs/articles/flutter-database.md","sourceDirName":"articles","slug":"/articles/flutter-database.html","permalink":"/articles/flutter-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Supercharge Flutter Apps with the RxDB Database","slug":"flutter-database.html","description":"Harness RxDB\'s reactive database to bring real-time, offline-first data storage and syncing to your next Flutter application."},"sidebar":"tutorialSidebar","previous":{"title":"Embedded Database, Real-time Speed - RxDB","permalink":"/articles/embedded-database.html"},"next":{"title":"RxDB - The Ultimate JS Frontend Database","permalink":"/articles/frontend-database.html"}}');var a=n(4848),i=n(8453);const t={title:"Supercharge Flutter Apps with the RxDB Database",slug:"flutter-database.html",description:"Harness RxDB's reactive database to bring real-time, offline-first data storage and syncing to your next Flutter application."},o="RxDB as a Database in a Flutter Application",l={},c=[{value:"Overview of Flutter Mobile Applications",id:"overview-of-flutter-mobile-applications",level:3},{value:"Importance of Databases in Flutter Applications",id:"importance-of-databases-in-flutter-applications",level:3},{value:"Introducing RxDB as a Database Solution",id:"introducing-rxdb-as-a-database-solution",level:3},{value:"Getting Started with RxDB",id:"getting-started-with-rxdb",level:2},{value:"What is RxDB?",id:"what-is-rxdb",level:3},{value:"Reactive Data Handling",id:"reactive-data-handling",level:3},{value:"Offline-First Approach",id:"offline-first-approach",level:3},{value:"Data Replication",id:"data-replication",level:3},{value:"Observable Queries",id:"observable-queries",level:3},{value:"RxDB vs. Other Flutter Database Options",id:"rxdb-vs-other-flutter-database-options",level:3},{value:"Using RxDB in a Flutter Application",id:"using-rxdb-in-a-flutter-application",level:2},{value:"How RxDB can run in Flutter",id:"how-rxdb-can-run-in-flutter",level:2},{value:"Different RxStorage layers for RxDB",id:"different-rxstorage-layers-for-rxdb",level:3},{value:"Synchronizing Data with RxDB between Clients and Servers",id:"synchronizing-data-with-rxdb-between-clients-and-servers",level:2},{value:"Offline-First Approach",id:"offline-first-approach-1",level:3},{value:"RxDB Replication Plugins",id:"rxdb-replication-plugins",level:3},{value:"Advanced RxDB Features and Techniques",id:"advanced-rxdb-features-and-techniques",level:2},{value:"Indexing and Performance Optimization",id:"indexing-and-performance-optimization",level:3},{value:"Encryption of Local Data",id:"encryption-of-local-data",level:3},{value:"Change Streams and Event Handling",id:"change-streams-and-event-handling",level:3},{value:"JSON Key Compression",id:"json-key-compression",level:3},{value:"Conclusion",id:"conclusion",level:2}];function d(e){const s={a:"a",admonition:"admonition",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.header,{children:(0,a.jsx)(s.h1,{id:"rxdb-as-a-database-in-a-flutter-application",children:"RxDB as a Database in a Flutter Application"})}),"\n",(0,a.jsxs)(s.p,{children:["In the world of mobile application development, Flutter has gained significant popularity due to its cross-platform capabilities and rich UI framework. When it comes to building feature-rich Flutter applications, the choice of a robust and efficient database is crucial. In this article, we will explore ",(0,a.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," as a database solution for Flutter applications. We'll delve into the core features of RxDB, its benefits over other database options, and how to integrate it into a Flutter app."]}),"\n",(0,a.jsx)(s.admonition,{type:"note",children:(0,a.jsxs)(s.p,{children:["You can find the source code for an example RxDB Flutter Application ",(0,a.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/flutter",children:"at the github repo"})]})}),"\n",(0,a.jsx)("center",{children:(0,a.jsx)("a",{href:"https://rxdb.info/",children:(0,a.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"RxDB Flutter Database",width:"220"})})}),"\n",(0,a.jsx)(s.h3,{id:"overview-of-flutter-mobile-applications",children:"Overview of Flutter Mobile Applications"}),"\n",(0,a.jsxs)(s.p,{children:["Flutter is an open-source UI software development kit created by Google that allows developers to build high-performance ",(0,a.jsx)(s.a,{href:"/articles/mobile-database.html",children:"mobile"})," applications for iOS and Android platforms using a single codebase. Flutter's framework provides a wide range of widgets and tools that enable developers to create visually appealing and responsive applications."]}),"\n",(0,a.jsx)("center",{children:(0,a.jsx)("img",{src:"../files/icons/flutter.svg",alt:"Flutter",width:"60"})}),"\n",(0,a.jsx)(s.h3,{id:"importance-of-databases-in-flutter-applications",children:"Importance of Databases in Flutter Applications"}),"\n",(0,a.jsx)(s.p,{children:"Databases play a vital role in Flutter applications by providing a persistent and reliable storage solution for storing and retrieving data. Whether it's user profiles, app settings, or complex data structures, a database helps in efficiently managing and organizing the application's data. Choosing the right database for a Flutter application can significantly impact the performance, scalability, and user experience of the app."}),"\n",(0,a.jsx)(s.h3,{id:"introducing-rxdb-as-a-database-solution",children:"Introducing RxDB as a Database Solution"}),"\n",(0,a.jsx)(s.p,{children:"RxDB is a powerful NoSQL database solution that is designed to work seamlessly with JavaScript-based frameworks, such as Flutter. It stands for Reactive Database and offers a variety of features that make it an excellent choice for building Flutter applications. RxDB combines the simplicity of JavaScript's document-based database model with the reactive programming paradigm, enabling developers to build real-time and offline-first applications with ease."}),"\n",(0,a.jsx)(s.h2,{id:"getting-started-with-rxdb",children:"Getting Started with RxDB"}),"\n",(0,a.jsx)(s.p,{children:"To understand how RxDB can be utilized in a Flutter application, let's explore its core features and advantages."}),"\n",(0,a.jsx)(s.h3,{id:"what-is-rxdb",children:"What is RxDB?"}),"\n",(0,a.jsxs)(s.p,{children:[(0,a.jsx)(s.a,{href:"https://rxdb.info/",children:"RxDB"})," is a client-side database built on top of ",(0,a.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"}),", which is a low-level ",(0,a.jsx)(s.a,{href:"/articles/browser-database.html",children:"browser-based database"})," API. It provides a simple and intuitive API for performing CRUD operations (Create, Read, Update, Delete) on documents. RxDB's underlying architecture allows for efficient handling of data synchronization between multiple clients and servers."]}),"\n",(0,a.jsx)("center",{children:(0,a.jsx)("a",{href:"https://rxdb.info/",children:(0,a.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"RxDB Flutter Database",width:"220"})})}),"\n",(0,a.jsx)(s.h3,{id:"reactive-data-handling",children:"Reactive Data Handling"}),"\n",(0,a.jsx)(s.p,{children:"One of the key strengths of RxDB is its reactive data handling. It leverages the power of Observables, a concept from reactive programming, to automatically update the UI in response to data changes. With RxDB, developers can define queries and subscribe to their results, ensuring that the UI is always in sync with the database."}),"\n",(0,a.jsx)(s.h3,{id:"offline-first-approach",children:"Offline-First Approach"}),"\n",(0,a.jsx)(s.p,{children:"RxDB follows an offline-first approach, making it ideal for building Flutter applications that need to function even without an internet connection. It allows data to be stored locally and seamlessly synchronizes it with the server when a connection is available. This ensures that users can access and interact with their data regardless of network availability."}),"\n",(0,a.jsx)(s.h3,{id:"data-replication",children:"Data Replication"}),"\n",(0,a.jsx)(s.p,{children:"Data replication is a critical aspect of building distributed applications. RxDB provides robust replication capabilities that enable synchronization of data between different clients and servers. With its replication plugins, RxDB simplifies the process of setting up real-time data synchronization, ensuring consistency across all connected devices."}),"\n",(0,a.jsx)(s.h3,{id:"observable-queries",children:"Observable Queries"}),"\n",(0,a.jsx)(s.p,{children:"RxDB introduces the concept of observable queries, which are queries that automatically update when the underlying data changes. This feature is particularly useful for keeping the UI up to date with the latest data. By subscribing to an observable query, developers can receive real-time updates and reflect them in the user interface without manual intervention."}),"\n",(0,a.jsx)(s.h3,{id:"rxdb-vs-other-flutter-database-options",children:"RxDB vs. Other Flutter Database Options"}),"\n",(0,a.jsx)(s.p,{children:"When considering database options for Flutter applications, developers often come across alternatives such as SQLite or LokiJS. While these databases have their merits, RxDB offers several advantages over them. RxDB's seamless integration with Flutter, its offline-first approach, reactive data handling, and built-in data replication make it a compelling choice for building feature-rich and scalable Flutter applications."}),"\n",(0,a.jsx)(s.h2,{id:"using-rxdb-in-a-flutter-application",children:"Using RxDB in a Flutter Application"}),"\n",(0,a.jsx)(s.p,{children:"Now that we understand the core features of RxDB, let's explore how to integrate it into a Flutter application."}),"\n",(0,a.jsx)(s.h2,{id:"how-rxdb-can-run-in-flutter",children:"How RxDB can run in Flutter"}),"\n",(0,a.jsxs)(s.p,{children:["RxDB is written in TypeScript and compiled to JavaScript. To run it in a Flutter application, the ",(0,a.jsx)(s.code,{children:"flutter_qjs"})," library is used to spawn a QuickJS JavaScript runtime. RxDB itself runs in that runtime and communicates with the flutter dart runtime. To store data persistent, the ",(0,a.jsx)(s.a,{href:"/rx-storage-lokijs.html",children:"LokiJS RxStorage"})," is used together with a custom storage adapter that persists the database inside of the ",(0,a.jsx)(s.code,{children:"shared_preferences"})," data."]}),"\n",(0,a.jsx)(s.p,{children:"To use RxDB, you have to create a compatible JavaScript file that creates your RxDatabase and starts some connectors which are used by Flutter to communicate with the JavaScript RxDB database via setFlutterRxDatabaseConnector()."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createRxDatabase"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getRxStorageLoki"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-lokijs'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" setFlutterRxDatabaseConnector"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" getLokijsAdapterFlutter"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/flutter'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// do all database creation stuff in this method."})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createDB"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(databaseName) {"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // create the RxDatabase"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // the database.name is variable so we can change it on the flutter side"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" databaseName"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLoki"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" adapter"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getLokijsAdapterFlutter"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" })"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" false"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" heroes"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" color"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 30"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" indexes"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'name'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'name'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'color'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// start the connector so that flutter can communicate with the JavaScript process"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"setFlutterRxDatabaseConnector"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" createDB"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})})]})})}),"\n",(0,a.jsxs)(s.p,{children:["Before you can use the JavaScript code, you have to bundle it into a single .js file. In this example we do that with webpack in a npm script here which bundles everything into the ",(0,a.jsx)(s.code,{children:"javascript/dist/index.js"})," file."]}),"\n",(0,a.jsx)(s.p,{children:"To allow Flutter to access that file during runtime, add it to the assets inside of your pubspec.yaml:"}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"yaml","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"yaml","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"flutter"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" assets"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" - "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"javascript/dist/index.js"})]})]})})}),"\n",(0,a.jsx)(s.p,{children:"Also you need to install RxDB in your flutter part of the application. First you have to use the rxdb dart package as a flutter dependency. Currently the package is not published at the dart pub.dev. Instead you have to install it from the local filesystem inside of your RxDB npm installation."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"yaml","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"yaml","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"# inside of pubspec.yaml"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"dependencies"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" rxdb"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" path"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" path/to/your/node_modules/rxdb/src/plugins/flutter/dart"})]})]})})}),"\n",(0,a.jsx)(s.p,{children:"Afterwards you can import the rxdb library in your dart code and connect to the JavaScript process from there. For reference, check out the lib/main.dart file."}),"\n",(0,a.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,a.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"dart","data-theme":"css-variables",children:(0,a.jsxs)(s.code,{"data-language":"dart","data-theme":"css-variables",style:{display:"grid"},children:[(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'package:rxdb/rxdb.dart'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// start the javascript process and connect to the database"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxDatabase"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"javascript/dist/index.js"'}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:", databaseName);"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// get a collection"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxCollection"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" database."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"getCollection"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'heroes'"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// insert a document"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" document "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"insert"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "id"'}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "zflutter-'}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"${"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"DateTime"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"now"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"()}"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:'"'}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:","})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "name"'}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" nameController.text,"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:' "color"'}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" colorController.text"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create a query"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxQuery"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxHeroDocType"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"> query "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" RxDatabaseState"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".collection."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"find"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"();"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// create list to store query results"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"List"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxDocument"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"<"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"RxHeroDocType"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:">> documents "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" [];"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// subscribe to a query"})}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"query.$()."}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:"listen"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"((results) {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" setState"}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(() {"})]}),"\n",(0,a.jsxs)(s.span,{"data-line":"",children:[(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" documents "}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" results;"})]}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,a.jsx)(s.span,{"data-line":"",children:(0,a.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,a.jsx)(s.h3,{id:"different-rxstorage-layers-for-rxdb",children:"Different RxStorage layers for RxDB"}),"\n",(0,a.jsx)(s.p,{children:"RxDB offers multiple storage options, known as RxStorage layers, to store data locally. These options include:"}),"\n",(0,a.jsxs)(s.ul,{children:["\n",(0,a.jsxs)(s.li,{children:[(0,a.jsx)(s.a,{href:"/rx-storage-lokijs.html",children:"LokiJS RxStorage"}),": LokiJS is an in-memory database that can be used as a ",(0,a.jsx)(s.a,{href:"/articles/browser-storage.html",children:"storage"})," layer for RxDB. It provides fast and efficient in-memory data management capabilities."]}),"\n",(0,a.jsxs)(s.li,{children:[(0,a.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite RxStorage"}),": SQLite is a popular and widely used ",(0,a.jsx)(s.a,{href:"/articles/embedded-database.html",children:"embedded database"})," that offers robust storage capabilities. RxDB utilizes SQLite as a storage layer to persist data on the device."]}),"\n",(0,a.jsxs)(s.li,{children:[(0,a.jsx)(s.a,{href:"/rx-storage-memory.html",children:"Memory RxStorage"}),": As the name suggests, Memory RxStorage stores data ",(0,a.jsx)(s.a,{href:"/articles/in-memory-nosql-database.html",children:"in memory"}),". While this option does not provide persistence, it can be useful for temporary or cache-based data storage.\nBy choosing the appropriate RxStorage layer based on the specific requirements of the application, developers can optimize performance and storage efficiency."]}),"\n"]}),"\n",(0,a.jsx)(s.h2,{id:"synchronizing-data-with-rxdb-between-clients-and-servers",children:"Synchronizing Data with RxDB between Clients and Servers"}),"\n",(0,a.jsx)(s.p,{children:"One of the key strengths of RxDB is its ability to synchronize data between multiple clients and servers seamlessly. Let's explore how this synchronization can be achieved."}),"\n",(0,a.jsx)(s.h3,{id:"offline-first-approach-1",children:"Offline-First Approach"}),"\n",(0,a.jsx)(s.p,{children:"RxDB's offline-first approach ensures that data can be accessed and modified even when there is no internet connection. Changes made offline are automatically synchronized with the server once a connection is reestablished. This ensures data consistency across all devices, providing a seamless user experience."}),"\n",(0,a.jsx)(s.h3,{id:"rxdb-replication-plugins",children:"RxDB Replication Plugins"}),"\n",(0,a.jsxs)(s.p,{children:["RxDB provides replication plugins that simplify the process of setting up data ",(0,a.jsx)(s.a,{href:"/replication.html",children:"synchronization between clients and servers"}),". These plugins offer various synchronization strategies, such as one-way replication, two-way replication, and conflict resolution mechanisms. By configuring the appropriate replication plugin, developers can easily establish real-time data synchronization in their Flutter applications."]}),"\n",(0,a.jsx)(s.h2,{id:"advanced-rxdb-features-and-techniques",children:"Advanced RxDB Features and Techniques"}),"\n",(0,a.jsx)(s.p,{children:"RxDB offers a range of advanced features and techniques that enhance its functionality and performance. Let's explore a few of these features:"}),"\n",(0,a.jsx)(s.h3,{id:"indexing-and-performance-optimization",children:"Indexing and Performance Optimization"}),"\n",(0,a.jsx)(s.p,{children:"Indexing is a technique used to optimize query performance by creating indexes on specific fields. RxDB allows developers to define indexes on document fields, improving the efficiency of queries and data retrieval."}),"\n",(0,a.jsx)(s.h3,{id:"encryption-of-local-data",children:"Encryption of Local Data"}),"\n",(0,a.jsxs)(s.p,{children:["To ensure data privacy and security, RxDB supports ",(0,a.jsx)(s.a,{href:"/encryption.html",children:"encryption of local data"}),". By encrypting the data stored on the device, developers can protect sensitive information and prevent unauthorized access."]}),"\n",(0,a.jsx)(s.h3,{id:"change-streams-and-event-handling",children:"Change Streams and Event Handling"}),"\n",(0,a.jsx)(s.p,{children:"RxDB provides change streams, which emit events whenever data changes occur. By leveraging change streams, developers can implement custom event handling logic, such as updating the UI or triggering background processes, in response to specific data changes."}),"\n",(0,a.jsx)(s.h3,{id:"json-key-compression",children:"JSON Key Compression"}),"\n",(0,a.jsxs)(s.p,{children:["To minimize storage requirements and optimize performance, RxDB offers ",(0,a.jsx)(s.a,{href:"/key-compression.html",children:"JSON key compression"}),". This feature reduces the size of keys used in the database, resulting in more efficient storage and improved query performance."]}),"\n",(0,a.jsx)(s.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,a.jsx)(s.p,{children:"RxDB offers a powerful and flexible database solution for Flutter applications. With its offline-first approach, real-time data synchronization, and reactive data handling capabilities, RxDB simplifies the development of feature-rich and scalable Flutter applications. By integrating RxDB into your Flutter projects, you can leverage its advanced features and techniques to build responsive and data-driven applications that provide an exceptional user experience."}),"\n",(0,a.jsx)(s.admonition,{type:"note",children:(0,a.jsxs)(s.p,{children:["You can find the source code for an example RxDB Flutter Application ",(0,a.jsx)(s.a,{href:"https://github.com/pubkey/rxdb/tree/master/examples/flutter",children:"at the github repo"})]})})]})}function h(e={}){const{wrapper:s}={...(0,i.R)(),...e.components};return s?(0,a.jsx)(s,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>t,x:()=>o});var r=n(6540);const a={},i=r.createContext(a);function t(e){const s=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function o(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:t(e.components),r.createElement(i.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/bdd39edd.10cefecb.js b/docs/assets/js/bdd39edd.10cefecb.js
deleted file mode 100644
index 141e3bccb7f..00000000000
--- a/docs/assets/js/bdd39edd.10cefecb.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[5852],{1541(e,a,s){s.r(a),s.d(a,{default:()=>l});var r=s(544),t=s(4848);function l(){return(0,r.default)({sem:{id:"gads",metaTitle:"The local Database for Browsers",appName:"Browser",title:(0,t.jsxs)(t.Fragment,{children:["The easiest way to ",(0,t.jsx)("b",{children:"store"})," and ",(0,t.jsx)("b",{children:"sync"})," Data in a Browser"]})}})}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/c0f75fb9.ea90dae3.js b/docs/assets/js/c0f75fb9.ea90dae3.js
deleted file mode 100644
index 1154a521b3b..00000000000
--- a/docs/assets/js/c0f75fb9.ea90dae3.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[833],{6440(e,s,n){n.r(s),n.d(s,{assets:()=>t,contentTitle:()=>l,default:()=>h,frontMatter:()=>a,metadata:()=>r,toc:()=>c});const r=JSON.parse('{"id":"articles/zero-latency-local-first","title":"Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression","description":"Build blazing-fast, zero-latency local first apps with RxDB. Gain instant UI responses, robust offline capabilities, end-to-end encryption, and data compression for streamlined performance.","source":"@site/docs/articles/zero-latency-local-first.md","sourceDirName":"articles","slug":"/articles/zero-latency-local-first.html","permalink":"/articles/zero-latency-local-first.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression","slug":"zero-latency-local-first.html","description":"Build blazing-fast, zero-latency local first apps with RxDB. Gain instant UI responses, robust offline capabilities, end-to-end encryption, and data compression for streamlined performance."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB \u2013 The Ultimate Offline Database with Sync and Encryption","permalink":"/articles/offline-database.html"},"next":{"title":"IndexedDB Max Storage Size Limit - Detailed Best Practices","permalink":"/articles/indexeddb-max-storage-limit.html"}}');var i=n(4848),o=n(8453);const a={title:"Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression",slug:"zero-latency-local-first.html",description:"Build blazing-fast, zero-latency local first apps with RxDB. Gain instant UI responses, robust offline capabilities, end-to-end encryption, and data compression for streamlined performance."},l="Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression",t={},c=[{value:"Why Zero Latency with a Local First Approach?",id:"why-zero-latency-with-a-local-first-approach",level:2},{value:"RxDB: Your Key to Zero-Latency Local First Apps",id:"rxdb-your-key-to-zero-latency-local-first-apps",level:2},{value:"Real-Time Sync and Offline-First",id:"real-time-sync-and-offline-first",level:3},{value:"Multiple Replication Plugins and Approaches",id:"multiple-replication-plugins-and-approaches",level:4},{value:"Example Setup of a local database",id:"example-setup-of-a-local-database",level:4},{value:"Example Setup of the replication",id:"example-setup-of-the-replication",level:4},{value:"Things you should also know about",id:"things-you-should-also-know-about",level:2},{value:"Optimistic UI on Local Data Changes",id:"optimistic-ui-on-local-data-changes",level:3},{value:"Conflict Handling",id:"conflict-handling",level:3},{value:"Schema Migrations",id:"schema-migrations",level:3},{value:"Advanced Features",id:"advanced-features",level:2},{value:"Setup Encryption",id:"setup-encryption",level:3},{value:"Setup Compression",id:"setup-compression",level:3},{value:"Different RxDB Storages Depending on the Runtime",id:"different-rxdb-storages-depending-on-the-runtime",level:2},{value:"Performance Considerations",id:"performance-considerations",level:2},{value:"Offloading Work from the Main Thread",id:"offloading-work-from-the-main-thread",level:3},{value:"Sharding or Memory-Mapped Storages",id:"sharding-or-memory-mapped-storages",level:3},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const s={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"zero-latency-local-first-apps-with-rxdb--sync-encryption-and-compression",children:"Zero Latency Local First Apps with RxDB \u2013 Sync, Encryption and Compression"})}),"\n",(0,i.jsxs)(s.p,{children:["Creating a ",(0,i.jsx)(s.strong,{children:"zero-latency local first"})," application involves ensuring that most (if not all) user interactions occur instantaneously, without waiting on remote network responses. This design drastically enhances user experience, allowing apps to remain responsive and functional even when offline or experiencing poor connectivity. As developers, we can achieve this by storing data ",(0,i.jsx)(s.strong,{children:"locally on the client"})," and synchronizing it to the backend in the background. ",(0,i.jsx)(s.strong,{children:"RxDB"})," (Reactive Database) offers a comprehensive set of features - covering replication, offline support, encryption, compression, conflict handling, and more - that make it straightforward to build such high-performing apps."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"/files/loading-spinner-not-needed.gif",alt:"loading spinner not needed",width:"300"})}),"\n",(0,i.jsx)(s.h2,{id:"why-zero-latency-with-a-local-first-approach",children:"Why Zero Latency with a Local First Approach?"}),"\n",(0,i.jsx)(s.p,{children:"In a traditional architecture, each user action triggers requests to a server for reads or writes. Despite network optimizations, unavoidable latencies can delay responses and disrupt the user flow. By contrast, a local first model maintains data in the client's environment (browser, mobile, desktop), drastically reducing user-perceived delays. Once the user re-connects or resumes activity online, changes propagate automatically to the server, eliminating manual synchronization overhead."}),"\n",(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Instant Responsiveness"}),": Because user actions (queries, updates, etc.) happen against a local datastore, UI updates do not wait on round-trip times."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Offline Operation"}),": Apps can continue to read and write data, even when there is zero connectivity."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Reduced Backend Load"}),": Instead of flooding the server with small requests, replication can combine and push or pull changes in batches."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Simplified Caching"}),": Instead of implementing multi-layer caching, local first transforms your data layer into a reliable, quickly accessible store for all user actions."]}),"\n"]}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"RxDB local Database",width:"220"})})}),"\n",(0,i.jsx)(s.h2,{id:"rxdb-your-key-to-zero-latency-local-first-apps",children:"RxDB: Your Key to Zero-Latency Local First Apps"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"RxDB"})," is a JavaScript-based NoSQL database designed for offline-first and real-time replication scenarios. It supports a range of environments - browsers (IndexedDB or OPFS), mobile (",(0,i.jsx)(s.a,{href:"/articles/ionic-storage.html",children:"Ionic"}),", ",(0,i.jsx)(s.a,{href:"/react-native-database.html",children:"React Native"}),"), ",(0,i.jsx)(s.a,{href:"/electron-database.html",children:"Electron"}),", Node.js - and is built around:"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Reactive Queries"})," that trigger UI updates upon data changes"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Schema-based NoSQL Documents"})," for flexible but robust data models"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/replication.html",children:"Advanced Sync Engine"}),": to synchronize with diverse backends"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Encryption"})," for secure data at rest"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Compression"})," to reduce local and network overhead"]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"real-time-sync-and-offline-first",children:"Real-Time Sync and Offline-First"}),"\n",(0,i.jsx)(s.p,{children:"RxDB's replication logic revolves around pulling down remote changes and pushing up local modifications. It maintains a checkpoint-based mechanism, so only new or updated documents flow between the client and server, reducing bandwidth usage and latency. This ensures:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Live Data"}),": Queries automatically reflect server-side changes once they arrive locally."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Background Updates"}),": No manual polling needed; replication streams or intervals handle synchronization."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Conflict Handling"})," (see below) ensures data merges gracefully when multiple clients edit the same document offline."]}),"\n"]}),"\n",(0,i.jsx)(s.h4,{id:"multiple-replication-plugins-and-approaches",children:"Multiple Replication Plugins and Approaches"}),"\n",(0,i.jsxs)(s.p,{children:["RxDB's flexible replication system lets you connect to different backends or even peer-to-peer networks. There are official plugins for ",(0,i.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB"}),", ",(0,i.jsx)(s.a,{href:"/replication-firestore.html",children:"Firestore"}),", ",(0,i.jsx)(s.a,{href:"/replication-graphql.html",children:"GraphQL"}),", ",(0,i.jsx)(s.a,{href:"/replication-webrtc.html",children:"WebRTC"}),", and more. Many developers create a ",(0,i.jsx)(s.strong,{children:"custom HTTP replication"})," to work with their existing REST-based backend, ensuring a painless integration that doesn't require adopting an entirely new server infrastructure."]}),"\n",(0,i.jsx)(s.h4,{id:"example-setup-of-a-local-database",children:"Example Setup of a local database"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/core'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" initZeroLocalDB"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"() {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Create a local RxDB instance using localstorage-based storage"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myZeroLocalDB'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // optional: password for encryption if needed"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Define one or more collections"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'task schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" done"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'boolean'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Reactive query - automatically updates on local or remote changes"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".tasks"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .find"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" .$ "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// returns an RxJS Observable"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" .subscribe"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(allTasks "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".log"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'All tasks updated:'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" allTasks);"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" db;"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["When offline, reads and writes to ",(0,i.jsx)(s.code,{children:"db.tasks"})," happen locally with near-zero delay. Once connectivity resumes, changes sync to the server automatically (if replication is configured)."]}),"\n",(0,i.jsx)(s.h4,{id:"example-setup-of-the-replication",children:"Example Setup of the replication"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { replicateRxCollection } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/replication'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" function"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" syncLocalTasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"(db) {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" replicateRxCollection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" collection"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:".tasks"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" replicationIdentifier"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'sync-tasks'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // Define how to pull server documents and push local documents"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" pull"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (lastCheckpoint"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" batchSize) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // logic to retrieve updated tasks from the server since lastCheckpoint"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" push"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" handler"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" async"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" (docs) "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // logic to post local changes to the server"})}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" live"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // continuously replicate"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" retryTime"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 5000"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:" // retry on errors or disconnections"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" });"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"}"})})]})})}),"\n",(0,i.jsx)(s.p,{children:"This replication seamlessly merges server-side and client-side changes. Your app remains responsive throughout, regardless of the network status."}),"\n",(0,i.jsx)(s.h2,{id:"things-you-should-also-know-about",children:"Things you should also know about"}),"\n",(0,i.jsx)(s.h3,{id:"optimistic-ui-on-local-data-changes",children:"Optimistic UI on Local Data Changes"}),"\n",(0,i.jsxs)(s.p,{children:["A local first approach, especially with RxDB, naturally supports an ",(0,i.jsx)(s.a,{href:"/articles/optimistic-ui.html",children:"optimistic UI"})," pattern. Because writes occur on the client, you can instantly reflect changes in the interface as soon as the user performs an action - no need to wait for server confirmation. For example, when a user updates a task document to done: true, the UI can re-render immediately with that new state. This even works across multiple browser tabs."]}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"/files/multiwindow.gif",alt:"RxDB multi tab",width:"450"})}),"\n",(0,i.jsxs)(s.p,{children:["If a server conflict arises later during replication, RxDB's ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"conflict handling"})," logic determines which changes to keep, and the UI can be updated accordingly. This is far more efficient than blocking the user or displaying a spinner while the backend processes the request."]}),"\n",(0,i.jsx)(s.h3,{id:"conflict-handling",children:"Conflict Handling"}),"\n",(0,i.jsx)(s.p,{children:"In local first models, conflicts emerge if multiple devices or clients edit the same document while offline. RxDB tracks document revisions so you can detect collisions and merge them effectively. By default, RxDB uses a last-write-wins approach, but developers can override it with a custom conflict handler. This provides fine-grained control - like merging partial fields, storing revision histories, or prompting users for resolution. Proper conflict handling keeps distributed data consistent across your entire system."}),"\n",(0,i.jsx)(s.h3,{id:"schema-migrations",children:"Schema Migrations"}),"\n",(0,i.jsx)(s.p,{children:"Over time, apps evolve - new fields, changed field types, or altered indexes. RxDB allows incremental schema migrations so you can upgrade a user's local data from one schema version to another. You might, for instance, rename a property or transform data formats. Once you define your migration strategy, RxDB automatically applies it upon app initialization, ensuring the local database's structure aligns with your latest codebase."}),"\n",(0,i.jsx)(s.h2,{id:"advanced-features",children:"Advanced Features"}),"\n",(0,i.jsx)(s.h3,{id:"setup-encryption",children:"Setup Encryption"}),"\n",(0,i.jsxs)(s.p,{children:["When storing data locally, you may handle user-sensitive information like PII (Personal Identifiable Information) or financial details. RxDB supports on-device ",(0,i.jsx)(s.a,{href:"/encryption.html",children:"encryption"})," to protect fields. For example, you can define:"]}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { wrappedKeyEncryptionCryptoJsStorage } "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/encryption-crypto-js'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" encryptedStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" wrappedKeyEncryptionCryptoJsStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"()"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'secureDB'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encryptedStorage"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myEncryptionPassword'"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" secrets"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'secrets schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" maxLength"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" secretField"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" required"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"]"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" encrypted"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" ["}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'secretField'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"] "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-comment)"},children:"// define which fields to encrypt"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsxs)(s.p,{children:["Then mark fields as ",(0,i.jsx)(s.code,{children:"encrypted"})," in the schema. This ensures data is unreadable on disk without the correct password."]}),"\n",(0,i.jsx)(s.h3,{id:"setup-compression",children:"Setup Compression"}),"\n",(0,i.jsx)(s.p,{children:"Local data can expand quickly, especially for large documents or repeated key names. RxDB's key compression feature replaces verbose field names with shorter tokens, decreasing storage usage and speeding up replication. You enable it by adding keyCompression: true to your collection schema:"}),"\n",(0,i.jsx)(s.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(s.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(s.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-function)"},children:".addCollections"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" logs"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" schema"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" title"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'log schema'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" version"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" keyCompression"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'object'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" primaryKey"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'id'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" properties"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" id"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:". maxLength: "}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-constant)"},children:"100"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" message"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'string'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(s.span,{"data-line":"",children:[(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" timestamp"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" { type"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'number'"}),(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})]}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(s.span,{"data-line":"",children:(0,i.jsx)(s.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(s.h2,{id:"different-rxdb-storages-depending-on-the-runtime",children:"Different RxDB Storages Depending on the Runtime"}),"\n",(0,i.jsx)(s.p,{children:"RxDB's storage layer is swappable, so you can pick the optimal adapter for each environment. Some common choices include:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB"})," in modern browsers (default)."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," (Origin Private File System) in browsers that support it for potentially better performance."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite"})," for mobile or desktop environments via the premium plugin, offering native-like speed on Android, iOS, or Electron."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/rx-storage-memory.html",children:"In-Memory"})," for tests or ephemeral data."]}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["By choosing a suitable storage layer, you can adapt your zero-latency local first design to any runtime - web, ",(0,i.jsx)(s.a,{href:"/articles/mobile-database.html",children:"mobile"}),", or server-like contexts in ",(0,i.jsx)(s.a,{href:"/nodejs-database.html",children:"Node.js"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"performance-considerations",children:"Performance Considerations"}),"\n",(0,i.jsxs)(s.p,{children:["Performant local data operations are crucial for a zero-latency experience. According to the RxDB ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"storage performance overview"}),", differences in underlying storages can significantly impact throughput and latency. For instance, IndexedDB typically performs well across modern browsers, ",(0,i.jsx)(s.a,{href:"/rx-storage-opfs.html",children:"OPFS"})," offers improved throughput in supporting browsers, and ",(0,i.jsx)(s.a,{href:"/rx-storage-sqlite.html",children:"SQLite storage"})," (a premium plugin) often delivers near-native speed for mobile or desktop."]}),"\n",(0,i.jsx)(s.h3,{id:"offloading-work-from-the-main-thread",children:"Offloading Work from the Main Thread"}),"\n",(0,i.jsxs)(s.p,{children:["In a browser environment, you can move database operations into a Web Worker using the ",(0,i.jsx)(s.a,{href:"/rx-storage-worker.html",children:"Worker RxStorage plugin"}),". This approach lets you keep heavy data processing off the main thread, ensuring the UI remains smooth and responsive. Complex queries or large write operations no longer cause stuttering in the user interface."]}),"\n",(0,i.jsx)(s.h3,{id:"sharding-or-memory-mapped-storages",children:"Sharding or Memory-Mapped Storages"}),"\n",(0,i.jsxs)(s.p,{children:["For large datasets or high concurrency, advanced techniques like ",(0,i.jsx)(s.a,{href:"/rx-storage-sharding.html",children:"sharding"})," collections across multiple storages or leveraging a ",(0,i.jsx)(s.a,{href:"/rx-storage-memory-mapped.html",children:"memory-mapped"})," variant can further boost performance. By splitting data into smaller subsets or streaming it only as needed, you can scale to handle complex usage scenarios without compromising on the zero-latency user experience."]}),"\n",(0,i.jsx)(s.h2,{id:"follow-up",children:"Follow Up"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:["Dive into the ",(0,i.jsx)(s.a,{href:"/quickstart.html",children:"RxDB Quickstart"})," to set up your own local first database."]}),"\n",(0,i.jsxs)(s.li,{children:["Explore ",(0,i.jsx)(s.a,{href:"/replication.html",children:"Replication Plugins"})," for syncing with platforms like ",(0,i.jsx)(s.a,{href:"/replication-couchdb.html",children:"CouchDB"}),", ",(0,i.jsx)(s.a,{href:"/articles/firestore-alternative.html",children:"Firestore"}),", or ",(0,i.jsx)(s.a,{href:"/replication-graphql.html",children:"GraphQL"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:["Check out Advanced ",(0,i.jsx)(s.a,{href:"/transactions-conflicts-revisions.html",children:"Conflict Handling"})," and ",(0,i.jsx)(s.a,{href:"/rx-storage-performance.html",children:"Performance Tuning"})," for big data sets or complex multi-user interactions."]}),"\n",(0,i.jsxs)(s.li,{children:["Join the RxDB Community on ",(0,i.jsx)(s.a,{href:"/code/",children:"GitHub"})," and ",(0,i.jsx)(s.a,{href:"/chat/",children:"Discord"})," to share insights, file issues, and learn from other developers building zero-latency solutions."]}),"\n",(0,i.jsx)(s.li,{}),"\n"]}),"\n",(0,i.jsxs)(s.p,{children:["By integrating RxDB into your stack, you achieve millisecond interactions, full ",(0,i.jsx)(s.a,{href:"/offline-first.html",children:"offline capabilities"}),", secure data at rest, and minimal overhead for large or distributed teams. This zero-latency local first architecture is the future of modern software - delivering a fluid, always-available user experience without overcomplicating the developer workflow."]})]})}function h(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453(e,s,n){n.d(s,{R:()=>a,x:()=>l});var r=n(6540);const i={},o=r.createContext(i);function a(e){const s=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:a(e.components),r.createElement(o.Provider,{value:s},e.children)}}}]);
\ No newline at end of file
diff --git a/docs/assets/js/c3bc9c50.8e3aa667.js b/docs/assets/js/c3bc9c50.8e3aa667.js
deleted file mode 100644
index 85860adcc41..00000000000
--- a/docs/assets/js/c3bc9c50.8e3aa667.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";(globalThis.webpackChunkrxdb=globalThis.webpackChunkrxdb||[]).push([[9167],{8097(e,a,n){n.r(a),n.d(a,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>t,metadata:()=>s,toc:()=>c});const s=JSON.parse('{"id":"articles/react-database","title":"RxDB as a Database for React Applications","description":"earn how the RxDB database supercharges React apps with offline access, real-time updates, and smooth data flow. Boost performance and engagement.","source":"@site/docs/articles/react-database.md","sourceDirName":"articles","slug":"/articles/react-database.html","permalink":"/articles/react-database.html","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"title":"RxDB as a Database for React Applications","slug":"react-database.html","description":"earn how the RxDB database supercharges React apps with offline access, real-time updates, and smooth data flow. Boost performance and engagement."},"sidebar":"tutorialSidebar","previous":{"title":"RxDB as a Database for Progressive Web Apps (PWA)","permalink":"/articles/progressive-web-app-database.html"},"next":{"title":"What Really Is a Realtime Database?","permalink":"/articles/realtime-database.html"}}');var i=n(4848),r=n(8453);const t={title:"RxDB as a Database for React Applications",slug:"react-database.html",description:"earn how the RxDB database supercharges React apps with offline access, real-time updates, and smooth data flow. Boost performance and engagement."},o="RxDB as a Database for React Applications",l={},c=[{value:"Introducing RxDB as a JavaScript Database",id:"introducing-rxdb-as-a-javascript-database",level:2},{value:"What is RxDB?",id:"what-is-rxdb",level:2},{value:"Reactive Data Handling",id:"reactive-data-handling",level:3},{value:"Local-First Approach",id:"local-first-approach",level:3},{value:"Data Replication",id:"data-replication",level:3},{value:"Observable Queries",id:"observable-queries",level:3},{value:"Multi-Tab Support",id:"multi-tab-support",level:3},{value:"RxDB vs. Other React Database Options",id:"rxdb-vs-other-react-database-options",level:3},{value:"IndexedDB in React and the Advantage of RxDB",id:"indexeddb-in-react-and-the-advantage-of-rxdb",level:3},{value:"Using RxDB in a React Application",id:"using-rxdb-in-a-react-application",level:3},{value:"Using RxDB React Hooks",id:"using-rxdb-react-hooks",level:3},{value:"Different RxStorage Layers for RxDB",id:"different-rxstorage-layers-for-rxdb",level:3},{value:"Synchronizing Data with RxDB between Clients and Servers",id:"synchronizing-data-with-rxdb-between-clients-and-servers",level:3},{value:"Advanced RxDB Features and Techniques",id:"advanced-rxdb-features-and-techniques",level:3},{value:"Indexing and Performance Optimization",id:"indexing-and-performance-optimization",level:3},{value:"JSON Key Compression",id:"json-key-compression",level:3},{value:"Change Streams and Event Handling",id:"change-streams-and-event-handling",level:3},{value:"Conclusion",id:"conclusion",level:2},{value:"Follow Up",id:"follow-up",level:2}];function d(e){const a={a:"a",code:"code",figure:"figure",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",span:"span",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(a.header,{children:(0,i.jsx)(a.h1,{id:"rxdb-as-a-database-for-react-applications",children:"RxDB as a Database for React Applications"})}),"\n",(0,i.jsx)(a.p,{children:"In the rapidly evolving landscape of web development, React has emerged as a cornerstone technology for building dynamic and responsive user interfaces. With the increasing complexity of modern web applications, efficient data management becomes pivotal. This article delves into the integration of RxDB, a potent client-side database, with React applications to optimize data handling and elevate the overall user experience."}),"\n",(0,i.jsx)(a.p,{children:"React has revolutionized the way web applications are built by introducing a component-based architecture. This approach enables developers to create reusable UI components that efficiently update in response to changes in data. The virtual DOM mechanism, a key feature of React, facilitates optimized rendering, enhancing performance and user interactivity."}),"\n",(0,i.jsx)(a.p,{children:"While React excels at managing the user interface, the need for efficient data storage and retrieval mechanisms is equally significant. A client-side database brings several advantages to React applications:"}),"\n",(0,i.jsxs)(a.ul,{children:["\n",(0,i.jsx)(a.li,{children:"Improved Performance: Local data storage reduces the need for frequent server requests, resulting in faster data retrieval and enhanced application responsiveness."}),"\n",(0,i.jsx)(a.li,{children:"Offline Capabilities: A client-side database enables offline access to data, allowing users to interact with the application even when they are disconnected from the internet."}),"\n",(0,i.jsx)(a.li,{children:"Real-Time Updates: With the ability to observe changes in data, client-side databases facilitate real-time updates to the UI, ensuring users are always presented with the latest information."}),"\n",(0,i.jsx)(a.li,{children:"Reduced Server Load: By handling data operations locally, client-side databases alleviate the load on the server, contributing to a more scalable architecture."}),"\n"]}),"\n",(0,i.jsx)(a.h2,{id:"introducing-rxdb-as-a-javascript-database",children:"Introducing RxDB as a JavaScript Database"}),"\n",(0,i.jsx)(a.p,{children:"RxDB, a powerful JavaScript database, has garnered attention as an optimal solution for managing data in React applications. Built on top of the IndexedDB standard, RxDB combines the principles of reactive programming with database management. Its core features include reactive data handling, offline-first capabilities, and robust data replication."}),"\n",(0,i.jsx)("center",{children:(0,i.jsx)("a",{href:"https://rxdb.info/",children:(0,i.jsx)("img",{src:"../files/logo/rxdb_javascript_database.svg",alt:"JavaScript React Database",width:"221"})})}),"\n",(0,i.jsx)(a.h2,{id:"what-is-rxdb",children:"What is RxDB?"}),"\n",(0,i.jsx)(a.p,{children:"RxDB, short for Reactive Database, is an open-source JavaScript database that seamlessly integrates reactive programming with database operations. It offers a comprehensive API for performing database actions and synchronizing data across clients and servers. RxDB's underlying philosophy revolves around observables, allowing developers to reactively manage data changes and create dynamic user interfaces."}),"\n",(0,i.jsx)(a.h3,{id:"reactive-data-handling",children:"Reactive Data Handling"}),"\n",(0,i.jsx)(a.p,{children:"One of RxDB's standout features is its support for reactive data handling. Traditional databases often require manual intervention for data fetching and updating, leading to complex and error-prone code. RxDB, however, automatically notifies subscribers whenever data changes occur, eliminating the need for explicit data manipulation. This reactive approach simplifies code and enhances the responsiveness of React components."}),"\n",(0,i.jsx)(a.h3,{id:"local-first-approach",children:"Local-First Approach"}),"\n",(0,i.jsxs)(a.p,{children:["RxDB embraces a ",(0,i.jsx)(a.a,{href:"/offline-first.html",children:"local-first"})," methodology, enabling applications to function seamlessly even in offline scenarios. By storing data locally, RxDB ensures that users can interact with the application and make updates regardless of internet connectivity. Once the connection is reestablished, RxDB synchronizes the local changes with the remote database, maintaining data consistency across devices."]}),"\n",(0,i.jsx)(a.h3,{id:"data-replication",children:"Data Replication"}),"\n",(0,i.jsx)(a.p,{children:"Data replication is a cornerstone of modern applications that require synchronization between multiple clients and servers. RxDB provides robust data replication mechanisms that facilitate real-time synchronization between different instances of the database. This ensures that changes made on one client are promptly propagated to others, contributing to a cohesive and unified user experience."}),"\n",(0,i.jsx)(a.h3,{id:"observable-queries",children:"Observable Queries"}),"\n",(0,i.jsx)(a.p,{children:"RxDB extends the concept of observables beyond data changes. It introduces observable queries, allowing developers to observe the results of database queries. This feature enables automatic updates to query results whenever relevant data changes occur. Observable queries simplify state management by eliminating the need to manually trigger updates in response to changing data."}),"\n",(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(a.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"await"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:"."}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"heroes"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" selector"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" healthpoints"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" $gt"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" 0"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" }"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"})"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:".$ "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// the $ returns an observable that emits each time the result set of the query changes"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".subscribe"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(aliveHeroes "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" console"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".dir"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(aliveHeroes));"})]})]})})}),"\n",(0,i.jsx)(a.h3,{id:"multi-tab-support",children:"Multi-Tab Support"}),"\n",(0,i.jsx)(a.p,{children:"Web applications often operate in multiple browser tabs or windows. RxDB accommodates this scenario by offering built-in multi-tab support. It ensures that data changes made in one tab are efficiently propagated to other tabs, maintaining data consistency and providing a seamless experience for users interacting with the application across different tabs."}),"\n",(0,i.jsx)("p",{align:"center",children:(0,i.jsx)("img",{src:"../files/multiwindow.gif",alt:"multi tab support",width:"450"})}),"\n",(0,i.jsx)(a.h3,{id:"rxdb-vs-other-react-database-options",children:"RxDB vs. Other React Database Options"}),"\n",(0,i.jsx)(a.p,{children:"While considering database options for React applications, RxDB stands out due to its unique combination of reactive programming and database capabilities. Unlike traditional solutions such as IndexedDB or Web Storage, which provide basic data storage, RxDB offers a dedicated database solution with advanced features. Additionally, while state management libraries like Redux and MobX can be adapted for database use, RxDB provides an integrated solution specifically designed for handling data."}),"\n",(0,i.jsx)(a.h3,{id:"indexeddb-in-react-and-the-advantage-of-rxdb",children:"IndexedDB in React and the Advantage of RxDB"}),"\n",(0,i.jsxs)(a.p,{children:["Using IndexedDB directly in React can be challenging due to its low-level, callback-based API which doesn't align neatly with modern React's Promise and async/await patterns. This intricacy often leads to bulky and complex implementations for developers. Also, when used wrong, IndexedDB can have a worse ",(0,i.jsx)(a.a,{href:"/slow-indexeddb.html",children:"performance profile"})," then it could have. In contrast, RxDB, with the ",(0,i.jsx)(a.a,{href:"/rx-storage-indexeddb.html",children:"IndexedDB RxStorage"})," and the ",(0,i.jsx)(a.a,{href:"/rx-storage-localstorage.html",children:"LocalStorage RxStorage"}),", abstracts these complexities, integrating reactive programming and providing a more streamlined experience for data management in React applications. Thus, RxDB offers a more intuitive approach, eliminating much of the manual overhead required with IndexedDB."]}),"\n",(0,i.jsx)(a.h3,{id:"using-rxdb-in-a-react-application",children:"Using RxDB in a React Application"}),"\n",(0,i.jsxs)(a.p,{children:["The process of integrating RxDB into a React application is straightforward. Begin by installing RxDB as a dependency:\n",(0,i.jsx)(a.code,{children:"npm install rxdb rxjs"}),"\nOnce installed, RxDB can be imported and initialized within your React components. The following code snippet illustrates a basic setup:"]}),"\n",(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"javascript","data-theme":"css-variables",children:(0,i.jsxs)(a.code,{"data-language":"javascript","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" { createRxDatabase } "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"import"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" { getRxStorageLocalstorage } "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"from"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'rxdb/plugins/storage-localstorage'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" db"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" await"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" createRxDatabase"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"({"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" name"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'heroesdb'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- name"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" storage"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" getRxStorageLocalstorage"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- RxStorage"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" password"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'myPassword'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- password (optional)"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" multiInstance"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- multiInstance (optional, default: true)"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" eventReduce"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" true"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:" // <- eventReduce (optional, default: false)"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" cleanupPolicy"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {} "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-comment)"},children:"// <- custom cleanup policy (optional) "})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"});"})})]})})}),"\n",(0,i.jsx)(a.h3,{id:"using-rxdb-react-hooks",children:"Using RxDB React Hooks"}),"\n",(0,i.jsxs)(a.p,{children:["The ",(0,i.jsx)(a.a,{href:"https://github.com/cvara/rxdb-hooks",children:"rxdb-hooks"})," package provides a set of React hooks that simplify data management within components. These hooks leverage RxDB's reactivity to automatically update components when data changes occur. The following example demonstrates the usage of the ",(0,i.jsx)(a.code,{children:"useRxCollection"})," and ",(0,i.jsx)(a.code,{children:"useRxQuery"})," hooks to query and observe a collection:"]}),"\n",(0,i.jsx)(a.figure,{"data-rehype-pretty-code-figure":"",children:(0,i.jsx)(a.pre,{style:{backgroundColor:"var(--shiki-background)",color:"var(--shiki-foreground)"},tabIndex:"0","data-language":"ts","data-theme":"css-variables",children:(0,i.jsxs)(a.code,{"data-language":"ts","data-theme":"css-variables",style:{display:"grid"},children:[(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" useRxCollection"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'characters'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" query"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" ="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" collection"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".find"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"()"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".where"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'affiliation'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:")"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:".equals"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"("}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:"'Jedi'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:");"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"const"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" result: "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:"characters"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" isFetching"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" fetchMore"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" isExhausted"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"} "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:" useRxQuery"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"(query"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" pageSize"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-constant)"},children:" 5"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" pagination"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:":"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Infinite'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"});"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"if"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" (isFetching) {"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" return"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-string-expression)"},children:" 'Loading...'"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:";"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"}"})}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:" "}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"return"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" <"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-function)"},children:"CharacterList"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:">"})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {characters.map((character"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-punctuation)"},children:","}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" index) "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"=>"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ("})]}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:" <"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"Character character"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"{character} key"}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"="}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:"{index} "}),(0,i.jsx)(a.span,{style:{color:"var(--shiki-token-keyword)"},children:"/>"})]}),"\n",(0,i.jsx)(a.span,{"data-line":"",children:(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" ))}"})}),"\n",(0,i.jsxs)(a.span,{"data-line":"",children:[(0,i.jsx)(a.span,{style:{color:"var(--shiki-foreground)"},children:" {!isExhausted && !0===e.exact))return z.set(e.pathname,e.pathname),e;const t=e.pathname.trim().replace(/(?:\/index)?\.html$/,"")||"/";return z.set(e.pathname,t),{...e,pathname:t}}((0,d.zy)());return(0,y.jsx)(G,{location:e,children:ne})}function oe(){return(0,y.jsx)(J.A,{children:(0,y.jsx)(B.l,{children:(0,y.jsxs)(D.x,{children:[(0,y.jsx)(w,{children:(0,y.jsxs)(j,{children:[(0,y.jsx)(Z,{}),(0,y.jsx)(F,{}),(0,y.jsx)(Q,{}),(0,y.jsx)(re,{})]})}),(0,y.jsx)(te,{})]})})})}var ae=n(4054);const ie=function(e){try{return document.createElement("link").relList.supports(e)}catch{return!1}}("prefetch")?function(e){return new Promise((t,n)=>{if("undefined"==typeof document)return void n();const r=document.createElement("link");r.setAttribute("rel","prefetch"),r.setAttribute("href",e),r.onload=()=>t(),r.onerror=()=>n();const o=document.getElementsByTagName("head")[0]??document.getElementsByName("script")[0]?.parentNode;o?.appendChild(r)})}:function(e){return new Promise((t,n)=>{const r=new XMLHttpRequest;r.open("GET",e,!0),r.withCredentials=!0,r.onload=()=>{200===r.status?t():n()},r.send(null)})};var le=n(6921);const se=new Set,ce=new Set,ue=()=>navigator.connection?.effectiveType.includes("2g")||navigator.connection?.saveData,de={prefetch:e=>{if(!(e=>!ue()&&!ce.has(e)&&!se.has(e))(e))return!1;se.add(e);const t=(0,f.u)(u.A,e).flatMap(e=>{return t=e.route.path,Object.entries(ae).filter(([e])=>e.replace(/-[^-]+$/,"")===t).flatMap(([,e])=>Object.values((0,le.A)(e)));var t});return Promise.all(t.map(e=>{const t=n.gca(e);return t&&!t.includes("undefined")?ie(t).catch(()=>{}):Promise.resolve()}))},preload:e=>!!(e=>!ue()&&!ce.has(e))(e)&&(ce.add(e),q(e))},fe=Object.freeze(de);function pe({children:e}){return"hash"===l.default.future.experimental_router?(0,y.jsx)(i.I9,{children:e}):(0,y.jsx)(i.Kd,{children:e})}const he=Boolean(!0);if(s.A.canUseDOM){window.docusaurus=fe;const e=document.getElementById("__docusaurus"),t=(0,y.jsx)(a.vd,{children:(0,y.jsx)(pe,{children:(0,y.jsx)(oe,{})})}),n=(e,t)=>{console.error("Docusaurus React Root onRecoverableError:",e,t)},i=()=>{if(window.docusaurusRoot)window.docusaurusRoot.render(t);else if(he)window.docusaurusRoot=o.hydrateRoot(e,t,{onRecoverableError:n});else{const r=o.createRoot(e,{onRecoverableError:n});r.render(t),window.docusaurusRoot=r}};q(window.location.pathname).then(()=>{(0,r.startTransition)(i)})}},6025(e,t,n){"use strict";n.d(t,{Ay:()=>l,hH:()=>i});var r=n(6540),o=n(4586),a=n(6654);function i(){const{siteConfig:e}=(0,o.A)(),{baseUrl:t,url:n}=e,i=e.future.experimental_router,l=(0,r.useCallback)((e,r)=>function({siteUrl:e,baseUrl:t,url:n,options:{forcePrependBaseUrl:r=!1,absolute:o=!1}={},router:i}){if(!n||n.startsWith("#")||(0,a.z)(n))return n;if("hash"===i)return n.startsWith("/")?`.${n}`:`./${n}`;if(r)return t+n.replace(/^\//,"");if(n===t.replace(/\/$/,""))return t;const l=n.startsWith(t)?n:t+n.replace(/^\//,"");return o?e+l:l}({siteUrl:n,baseUrl:t,url:e,options:r,router:i}),[n,t,i]);return{withBaseUrl:l}}function l(e,t={}){const{withBaseUrl:n}=i();return n(e,t)}},6125(e,t,n){"use strict";n.d(t,{o:()=>a,x:()=>i});var r=n(6540),o=n(4848);const a=r.createContext(!1);function i({children:e}){const[t,n]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{n(!0)},[]),(0,o.jsx)(a.Provider,{value:t,children:e})}},6221(e,t,n){"use strict";var r=n(6540);function o(e){var t="https://react.dev/errors/"+e;if(1a});var r=n(5947),o=n.n(r);o().configure({showSpinner:!1});const a={onRouteUpdate({location:e,previousLocation:t}){if(t&&e.pathname!==t.pathname){const e=window.setTimeout(()=>{o().start()},200);return()=>window.clearTimeout(e)}},onRouteDidUpdate(){o().done()}}},6342(e,t,n){"use strict";n.d(t,{p:()=>o});var r=n(4586);function o(){return(0,r.A)().siteConfig.themeConfig}},6347(e,t,n){"use strict";n.d(t,{B6:()=>k,Ix:()=>y,W6:()=>A,XZ:()=>v,dO:()=>T,qh:()=>S,zy:()=>N});var r=n(7387),o=n(6540),a=n(5556),i=n.n(a),l=n(1513),s=n(1561),c=n(8168),u=n(5302),d=n.n(u),f=(n(7564),n(8587)),p=(n(4146),1073741823),h="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof globalThis?globalThis:{};var m=o.createContext||function(e,t){var n,a,l="__create-react-context-"+function(){var e="__global_unique_id__";return h[e]=(h[e]||0)+1}()+"__",s=function(e){function n(){for(var t,n,r,o=arguments.length,a=new Array(o),i=0;ia});var r=n(6540),o=n(4848);function a(e){const t=!!e.img,[n,a]=(0,r.useState)(!1);return(0,o.jsxs)("div",{style:{display:"inline-flex",alignItems:"center",verticalAlign:"bottom",background:n?"#fff":e.dark?"var(--bg-color)":"var(--bg-color-dark)",height:e.border?37:41,paddingTop:0,paddingBottom:0,borderRadius:20,textAlign:"center",color:n?e.dark?"var(--bg-color-dark)":"var(--bg-color)":"white",width:"auto",fontWeight:t?800:500,whiteSpace:"nowrap",boxSizing:"border-box",userSelect:"none",border:e.border?"2px solid var(--White, #FFF)":"none",transition:"all 0.2s ease-in-out",lineHeight:"100%"},className:"margin-right-10-6 "+(e.wideMode?"font-20-14":"font-16-14")+" "+(e.wideMode?"padding-side-16-12":"padding-side-10-12")+" "+(e.wideMode?"margin-bottom-16-10":"margin-bottom-12"),onMouseEnter:()=>a(!0),onMouseLeave:()=>a(!1),children:[t&&("string"==typeof e.img?(0,o.jsx)("img",{draggable:!1,src:e.img,loading:"lazy",alt:"",className:e.wideMode?"margin-right-8":"margin-right-6-8",style:{height:"60%",width:24,marginRight:6,display:"block",objectFit:"contain",filter:n?"invert(1)":void 0,transition:"filter 0.2s ease-in-out"}}):(0,o.jsx)("span",{className:e.wideMode?"margin-right-8":"margin-right-6-8",style:{height:"60%",width:24,marginRight:6,display:"block",objectFit:"contain",alignItems:"center",filter:n?"invert(1)":void 0,transition:"filter 0.2s ease-in-out"},children:e.img})),(0,o.jsx)("div",{style:{display:"flex"},children:e.children})]})}},6540(e,t,n){"use strict";e.exports=n(9869)},6588(e,t,n){"use strict";n.d(t,{P_:()=>i,kh:()=>a});var r=n(4586),o=n(7065);function a(e,t={}){const n=function(){const{globalData:e}=(0,r.A)();return e}()[e];if(!n&&t.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin.`);return n}function i(e,t=o.W,n={}){const r=a(e),i=r?.[t];if(!i&&n.failfast)throw new Error(`Docusaurus plugin global data not found for "${e}" plugin with id "${t}".`);return i}},6654(e,t,n){"use strict";function r(e){return/^(?:\w*:|\/\/)/.test(e)}function o(e){return void 0!==e&&!r(e)}n.d(t,{A:()=>o,z:()=>r})},6662(e,t,n){"use strict";n.d(t,{L:()=>a,u:()=>o});var r=n(4848);function o({size:e=50,onClick:t}){const n={container:{width:e,height:e,borderRadius:"50%",display:"flex",alignItems:"center",justifyContent:"center",cursor:"pointer",userSelect:"none",border:"2px solid var(--White, #FFF)",background:"linear-gradient(90deg, #ED168F 0%, #B2218B 100%)"},icon:{width:15,display:"block"}};return(0,r.jsx)("div",{onClick:t,children:(0,r.jsx)(a,{style:n.container})})}function a({style:e,className:t}){return(0,r.jsx)("div",{style:e,className:t,children:(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"15",height:"25",viewBox:"0 0 15 25",fill:"none",style:{marginLeft:3,height:"100%"},children:(0,r.jsx)("path",{d:"M10 10V5H5V0H0V5V20V25H5V20H10V15H15V10H10Z",fill:"white"})})})}},6712(e,t,n){"use strict";n.d(t,{m:()=>a});var r=n(2236),o=n(2332);function a(e){o.f.setTimeout(function(){var t=r.$.onUnhandledError;if(!t)throw e;t(e)})}},6803(e,t,n){"use strict";n.d(t,{A:()=>a});var r=n(6540),o=n(3102);function a(){const e=r.useContext(o.o);if(!e)throw new Error("Unexpected: no Docusaurus route context found");return e}},6921(e,t,n){"use strict";n.d(t,{A:()=>r});function r(e){const t={};return function e(n,r){Object.entries(n).forEach(([n,o])=>{const a=r?`${r}.${n}`:n;var i;"object"==typeof(i=o)&&i&&Object.keys(i).length>0?e(o,a):t[a]=o})}(e),t}},6925(e){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},6942(e,t){var n;!function(){"use strict";var r={}.hasOwnProperty;function o(){for(var e="",t=0;td,l:()=>f});var r=n(6540),o=n(4784);const a=JSON.parse('{"docusaurus-plugin-content-docs":{"default":{"path":"/","versions":[{"name":"current","label":"Next","isLast":true,"path":"/","mainDocId":"overview","docs":[{"id":"adapters","path":"/adapters.html"},{"id":"alternatives","path":"/alternatives.html","sidebar":"tutorialSidebar"},{"id":"articles/angular-database","path":"/articles/angular-database.html","sidebar":"tutorialSidebar"},{"id":"articles/angular-indexeddb","path":"/articles/angular-indexeddb.html","sidebar":"tutorialSidebar"},{"id":"articles/browser-database","path":"/articles/browser-database.html","sidebar":"tutorialSidebar"},{"id":"articles/browser-storage","path":"/articles/browser-storage.html","sidebar":"tutorialSidebar"},{"id":"articles/data-base","path":"/articles/data-base.html","sidebar":"tutorialSidebar"},{"id":"articles/embedded-database","path":"/articles/embedded-database.html","sidebar":"tutorialSidebar"},{"id":"articles/firebase-realtime-database-alternative","path":"/articles/firebase-realtime-database-alternative.html","sidebar":"tutorialSidebar"},{"id":"articles/firestore-alternative","path":"/articles/firestore-alternative.html","sidebar":"tutorialSidebar"},{"id":"articles/flutter-database","path":"/articles/flutter-database.html","sidebar":"tutorialSidebar"},{"id":"articles/frontend-database","path":"/articles/frontend-database.html","sidebar":"tutorialSidebar"},{"id":"articles/ideas","path":"/articles/ideas"},{"id":"articles/in-memory-nosql-database","path":"/articles/in-memory-nosql-database.html","sidebar":"tutorialSidebar"},{"id":"articles/indexeddb-max-storage-limit","path":"/articles/indexeddb-max-storage-limit.html","sidebar":"tutorialSidebar"},{"id":"articles/ionic-database","path":"/articles/ionic-database.html","sidebar":"tutorialSidebar"},{"id":"articles/ionic-storage","path":"/articles/ionic-storage.html","sidebar":"tutorialSidebar"},{"id":"articles/javascript-vector-database","path":"/articles/javascript-vector-database.html","sidebar":"tutorialSidebar"},{"id":"articles/jquery-database","path":"/articles/jquery-database.html","sidebar":"tutorialSidebar"},{"id":"articles/json-based-database","path":"/articles/json-based-database.html","sidebar":"tutorialSidebar"},{"id":"articles/json-database","path":"/articles/json-database.html","sidebar":"tutorialSidebar"},{"id":"articles/local-database","path":"/articles/local-database.html","sidebar":"tutorialSidebar"},{"id":"articles/local-first-future","path":"/articles/local-first-future.html","sidebar":"tutorialSidebar"},{"id":"articles/localstorage","path":"/articles/localstorage.html","sidebar":"tutorialSidebar"},{"id":"articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm","path":"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html","sidebar":"tutorialSidebar"},{"id":"articles/mobile-database","path":"/articles/mobile-database.html","sidebar":"tutorialSidebar"},{"id":"articles/offline-database","path":"/articles/offline-database.html","sidebar":"tutorialSidebar"},{"id":"articles/optimistic-ui","path":"/articles/optimistic-ui.html","sidebar":"tutorialSidebar"},{"id":"articles/progressive-web-app-database","path":"/articles/progressive-web-app-database.html","sidebar":"tutorialSidebar"},{"id":"articles/react-database","path":"/articles/react-database.html","sidebar":"tutorialSidebar"},{"id":"articles/react-indexeddb","path":"/articles/react-indexeddb.html","sidebar":"tutorialSidebar"},{"id":"articles/react-native-encryption","path":"/articles/react-native-encryption.html","sidebar":"tutorialSidebar"},{"id":"articles/reactjs-storage","path":"/articles/reactjs-storage.html","sidebar":"tutorialSidebar"},{"id":"articles/realtime-database","path":"/articles/realtime-database.html","sidebar":"tutorialSidebar"},{"id":"articles/vue-database","path":"/articles/vue-database.html","sidebar":"tutorialSidebar"},{"id":"articles/vue-indexeddb","path":"/articles/vue-indexeddb.html","sidebar":"tutorialSidebar"},{"id":"articles/websockets-sse-polling-webrtc-webtransport","path":"/articles/websockets-sse-polling-webrtc-webtransport.html","sidebar":"tutorialSidebar"},{"id":"articles/zero-latency-local-first","path":"/articles/zero-latency-local-first.html","sidebar":"tutorialSidebar"},{"id":"backup","path":"/backup.html","sidebar":"tutorialSidebar"},{"id":"capacitor-database","path":"/capacitor-database.html","sidebar":"tutorialSidebar"},{"id":"cleanup","path":"/cleanup.html","sidebar":"tutorialSidebar"},{"id":"contribute","path":"/contribution.html","sidebar":"tutorialSidebar"},{"id":"crdt","path":"/crdt.html","sidebar":"tutorialSidebar"},{"id":"data-migration","path":"/data-migration.html"},{"id":"dev-mode","path":"/dev-mode.html","sidebar":"tutorialSidebar"},{"id":"downsides-of-offline-first","path":"/downsides-of-offline-first.html","sidebar":"tutorialSidebar"},{"id":"electron","path":"/electron.html","sidebar":"tutorialSidebar"},{"id":"electron-database","path":"/electron-database.html","sidebar":"tutorialSidebar"},{"id":"encryption","path":"/encryption.html","sidebar":"tutorialSidebar"},{"id":"errors","path":"/errors.html","sidebar":"tutorialSidebar"},{"id":"fulltext-search","path":"/fulltext-search.html","sidebar":"tutorialSidebar"},{"id":"install","path":"/install.html","sidebar":"tutorialSidebar"},{"id":"key-compression","path":"/key-compression.html","sidebar":"tutorialSidebar"},{"id":"leader-election","path":"/leader-election.html","sidebar":"tutorialSidebar"},{"id":"logger","path":"/logger.html","sidebar":"tutorialSidebar"},{"id":"middleware","path":"/middleware.html","sidebar":"tutorialSidebar"},{"id":"migration-schema","path":"/migration-schema.html","sidebar":"tutorialSidebar"},{"id":"migration-storage","path":"/migration-storage.html","sidebar":"tutorialSidebar"},{"id":"nodejs-database","path":"/nodejs-database.html","sidebar":"tutorialSidebar"},{"id":"nosql-performance-tips","path":"/nosql-performance-tips.html","sidebar":"tutorialSidebar"},{"id":"offline-first","path":"/offline-first.html","sidebar":"tutorialSidebar"},{"id":"orm","path":"/orm.html","sidebar":"tutorialSidebar"},{"id":"overview","path":"/overview.html","sidebar":"tutorialSidebar"},{"id":"plugins","path":"/plugins.html","sidebar":"tutorialSidebar"},{"id":"population","path":"/population.html","sidebar":"tutorialSidebar"},{"id":"query-cache","path":"/query-cache.html","sidebar":"tutorialSidebar"},{"id":"query-optimizer","path":"/query-optimizer.html","sidebar":"tutorialSidebar"},{"id":"quickstart","path":"/quickstart.html","sidebar":"tutorialSidebar"},{"id":"react","path":"/react.html","sidebar":"tutorialSidebar"},{"id":"react-native-database","path":"/react-native-database.html","sidebar":"tutorialSidebar"},{"id":"reactivity","path":"/reactivity.html","sidebar":"tutorialSidebar"},{"id":"releases/10.0.0","path":"/releases/10.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/11.0.0","path":"/releases/11.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/12.0.0","path":"/releases/12.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/13.0.0","path":"/releases/13.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/14.0.0","path":"/releases/14.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/15.0.0","path":"/releases/15.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/16.0.0","path":"/releases/16.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/17.0.0","path":"/releases/17.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/8.0.0","path":"/releases/8.0.0.html","sidebar":"tutorialSidebar"},{"id":"releases/9.0.0","path":"/releases/9.0.0.html","sidebar":"tutorialSidebar"},{"id":"replication","path":"/replication.html","sidebar":"tutorialSidebar"},{"id":"replication-appwrite","path":"/replication-appwrite.html","sidebar":"tutorialSidebar"},{"id":"replication-couchdb","path":"/replication-couchdb.html","sidebar":"tutorialSidebar"},{"id":"replication-firestore","path":"/replication-firestore.html","sidebar":"tutorialSidebar"},{"id":"replication-graphql","path":"/replication-graphql.html","sidebar":"tutorialSidebar"},{"id":"replication-http","path":"/replication-http.html","sidebar":"tutorialSidebar"},{"id":"replication-mongodb","path":"/replication-mongodb.html","sidebar":"tutorialSidebar"},{"id":"replication-nats","path":"/replication-nats.html","sidebar":"tutorialSidebar"},{"id":"replication-p2p","path":"/replication-p2p.html"},{"id":"replication-server","path":"/replication-server.html","sidebar":"tutorialSidebar"},{"id":"replication-supabase","path":"/replication-supabase.html","sidebar":"tutorialSidebar"},{"id":"replication-webrtc","path":"/replication-webrtc.html","sidebar":"tutorialSidebar"},{"id":"replication-websocket","path":"/replication-websocket.html","sidebar":"tutorialSidebar"},{"id":"rx-attachment","path":"/rx-attachment.html","sidebar":"tutorialSidebar"},{"id":"rx-collection","path":"/rx-collection.html","sidebar":"tutorialSidebar"},{"id":"rx-database","path":"/rx-database.html","sidebar":"tutorialSidebar"},{"id":"rx-document","path":"/rx-document.html","sidebar":"tutorialSidebar"},{"id":"rx-local-document","path":"/rx-local-document.html","sidebar":"tutorialSidebar"},{"id":"rx-pipeline","path":"/rx-pipeline.html","sidebar":"tutorialSidebar"},{"id":"rx-query","path":"/rx-query.html","sidebar":"tutorialSidebar"},{"id":"rx-schema","path":"/rx-schema.html","sidebar":"tutorialSidebar"},{"id":"rx-server","path":"/rx-server.html","sidebar":"tutorialSidebar"},{"id":"rx-server-scaling","path":"/rx-server-scaling.html","sidebar":"tutorialSidebar"},{"id":"rx-state","path":"/rx-state.html","sidebar":"tutorialSidebar"},{"id":"rx-storage","path":"/rx-storage.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-denokv","path":"/rx-storage-denokv.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-dexie","path":"/rx-storage-dexie.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-filesystem-node","path":"/rx-storage-filesystem-node.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-foundationdb","path":"/rx-storage-foundationdb.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-indexeddb","path":"/rx-storage-indexeddb.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-localstorage","path":"/rx-storage-localstorage.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-localstorage-meta-optimizer","path":"/rx-storage-localstorage-meta-optimizer.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-lokijs","path":"/rx-storage-lokijs.html"},{"id":"rx-storage-memory","path":"/rx-storage-memory.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-memory-mapped","path":"/rx-storage-memory-mapped.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-memory-synced","path":"/rx-storage-memory-synced.html"},{"id":"rx-storage-mongodb","path":"/rx-storage-mongodb.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-opfs","path":"/rx-storage-opfs.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-performance","path":"/rx-storage-performance.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-pouchdb","path":"/rx-storage-pouchdb.html"},{"id":"rx-storage-remote","path":"/rx-storage-remote.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-sharding","path":"/rx-storage-sharding.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-shared-worker","path":"/rx-storage-shared-worker.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-sqlite","path":"/rx-storage-sqlite.html","sidebar":"tutorialSidebar"},{"id":"rx-storage-worker","path":"/rx-storage-worker.html","sidebar":"tutorialSidebar"},{"id":"rxdb-tradeoffs","path":"/rxdb-tradeoffs.html"},{"id":"schema-validation","path":"/schema-validation.html","sidebar":"tutorialSidebar"},{"id":"slow-indexeddb","path":"/slow-indexeddb.html","sidebar":"tutorialSidebar"},{"id":"third-party-plugins","path":"/third-party-plugins.html","sidebar":"tutorialSidebar"},{"id":"transactions-conflicts-revisions","path":"/transactions-conflicts-revisions.html","sidebar":"tutorialSidebar"},{"id":"tutorials/typescript","path":"/tutorials/typescript.html","sidebar":"tutorialSidebar"},{"id":"why-nosql","path":"/why-nosql.html","sidebar":"tutorialSidebar"}],"draftIds":[],"sidebars":{"tutorialSidebar":{"link":{"path":"/overview.html","label":"Overview"}}}}],"breadcrumbs":false}},"docusaurus-plugin-google-gtag":{"default":{"trackingID":["G-62D63SY3S0"],"anonymizeIP":false,"id":"default"}},"docusaurus-plugin-google-tag-manager":{"default":{"containerId":"GTM-PL63TR5","id":"default"}},"docusaurus-lunr-search":{"default":{"fileNames":{"searchDoc":"search-doc-1769435561373.json","lunrIndex":"lunr-index-1769435561373.json"}}}}'),i=JSON.parse('{"defaultLocale":"en","locales":["en"],"path":"i18n","currentLocale":"en","localeConfigs":{"en":{"label":"English","direction":"ltr","htmlLang":"en","calendar":"gregory","path":"en","translate":false,"url":"https://rxdb.info","baseUrl":"/"}}}');var l=n(2654);const s=JSON.parse('{"docusaurusVersion":"3.9.2","siteVersion":"0.0.0","pluginVersions":{"docusaurus-plugin-content-docs":{"type":"package","name":"@docusaurus/plugin-content-docs","version":"3.9.2"},"docusaurus-plugin-content-blog":{"type":"package","name":"@docusaurus/plugin-content-blog","version":"3.9.2"},"docusaurus-plugin-content-pages":{"type":"package","name":"@docusaurus/plugin-content-pages","version":"3.9.2"},"docusaurus-plugin-google-gtag":{"type":"package","name":"@docusaurus/plugin-google-gtag","version":"3.9.2"},"docusaurus-plugin-google-tag-manager":{"type":"package","name":"@docusaurus/plugin-google-tag-manager","version":"3.9.2"},"docusaurus-plugin-sitemap":{"type":"package","name":"@docusaurus/plugin-sitemap","version":"3.9.2"},"docusaurus-plugin-svgr":{"type":"package","name":"@docusaurus/plugin-svgr","version":"3.9.2"},"docusaurus-theme-classic":{"type":"package","name":"@docusaurus/theme-classic","version":"3.9.2"},"docusaurus-plugin-llms":{"type":"package","name":"docusaurus-plugin-llms","version":"0.2.2"},"docusaurus-lunr-search":{"type":"package","name":"docusaurus-lunr-search","version":"3.3.1"},"custom-webpack-tweaks":{"type":"local"}}}');var c=n(4848);const u={siteConfig:o.default,siteMetadata:s,globalData:a,i18n:i,codeTranslations:l},d=r.createContext(u);function f({children:e}){return(0,c.jsx)(d.Provider,{value:u,children:e})}},7065(e,t,n){"use strict";n.d(t,{W:()=>r});const r="default"},7387(e,t,n){"use strict";n.d(t,{A:()=>o});var r=n(3662);function o(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,(0,r.A)(e,t)}},7485(e,t,n){"use strict";n.d(t,{$Z:()=>i,Hl:()=>l,jy:()=>s});var r=n(6540),o=n(6347),a=n(9532);function i(e){!function(e){const t=(0,o.W6)(),n=(0,a._q)(e);(0,r.useEffect)(()=>t.block((e,t)=>n(e,t)),[t,n])}((t,n)=>{if("POP"===n)return e(t,n)})}function l(e){const t=(0,o.W6)();return(0,r.useSyncExternalStore)(t.listen,()=>e(t),()=>e({...t,location:{...t.location,search:"",hash:"",state:void 0}}))}function s(e,t){const n=function(e,t){const n=new URLSearchParams;for(const r of e)for(const[e,o]of r.entries())"append"===t?n.append(e,o):n.set(e,o);return n}(e.map(e=>new URLSearchParams(e??"")),t),r=n.toString();return r?`?${r}`:r}},7489(e,t,n){"use strict";n.d(t,{A:()=>m});var r=n(6540),o=n(8193),a=n(5260),i=n(440),l=n(8711),s=n(3102),c=n(4848);function u({error:e,tryAgain:t}){return(0,c.jsxs)("div",{style:{display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"flex-start",minHeight:"100vh",width:"100%",maxWidth:"80ch",fontSize:"20px",margin:"0 auto",padding:"1rem"},children:[(0,c.jsx)("h1",{style:{fontSize:"3rem"},children:"This page crashed"}),(0,c.jsx)("button",{type:"button",onClick:t,style:{margin:"1rem 0",fontSize:"2rem",cursor:"pointer",borderRadius:20,padding:"1rem"},children:"Try again"}),(0,c.jsx)(d,{error:e})]})}function d({error:e}){const t=(0,i.rA)(e).map(e=>e.message).join("\n\nCause:\n");return(0,c.jsx)("p",{style:{whiteSpace:"pre-wrap"},children:t})}function f({children:e}){return(0,c.jsx)(s.W,{value:{plugin:{name:"docusaurus-core-error-boundary",id:"default"}},children:e})}function p({error:e,tryAgain:t}){return(0,c.jsx)(f,{children:(0,c.jsxs)(m,{fallback:()=>(0,c.jsx)(u,{error:e,tryAgain:t}),children:[(0,c.jsx)(a.A,{children:(0,c.jsx)("title",{children:"Page Error"})}),(0,c.jsx)(l.A,{children:(0,c.jsx)(u,{error:e,tryAgain:t})})]})})}const h=e=>(0,c.jsx)(p,{...e});class m extends r.Component{constructor(e){super(e),this.state={error:null}}componentDidCatch(e){o.A.canUseDOM&&this.setState({error:e})}render(){const{children:e}=this.props,{error:t}=this.state;if(t){const e={error:t,tryAgain:()=>this.setState({error:null})};return(this.props.fallback??h)(e)}return e??null}}},7559(e,t,n){"use strict";n.d(t,{G:()=>r});const r={page:{blogListPage:"blog-list-page",blogPostPage:"blog-post-page",blogTagsListPage:"blog-tags-list-page",blogTagPostListPage:"blog-tags-post-list-page",blogAuthorsListPage:"blog-authors-list-page",blogAuthorsPostsPage:"blog-authors-posts-page",docsDocPage:"docs-doc-page",docsTagsListPage:"docs-tags-list-page",docsTagDocListPage:"docs-tags-doc-list-page",mdxPage:"mdx-page"},wrapper:{main:"main-wrapper",blogPages:"blog-wrapper",docsPages:"docs-wrapper",mdxPages:"mdx-wrapper"},common:{editThisPage:"theme-edit-this-page",lastUpdated:"theme-last-updated",backToTopButton:"theme-back-to-top-button",codeBlock:"theme-code-block",admonition:"theme-admonition",unlistedBanner:"theme-unlisted-banner",draftBanner:"theme-draft-banner",admonitionType:e=>`theme-admonition-${e}`},announcementBar:{container:"theme-announcement-bar"},tabs:{container:"theme-tabs-container"},layout:{navbar:{container:"theme-layout-navbar",containerLeft:"theme-layout-navbar-left",containerRight:"theme-layout-navbar-right",mobileSidebar:{container:"theme-layout-navbar-sidebar",panel:"theme-layout-navbar-sidebar-panel"}},main:{container:"theme-layout-main"},footer:{container:"theme-layout-footer",column:"theme-layout-footer-column"}},docs:{docVersionBanner:"theme-doc-version-banner",docVersionBadge:"theme-doc-version-badge",docBreadcrumbs:"theme-doc-breadcrumbs",docMarkdown:"theme-doc-markdown",docTocMobile:"theme-doc-toc-mobile",docTocDesktop:"theme-doc-toc-desktop",docFooter:"theme-doc-footer",docFooterTagsRow:"theme-doc-footer-tags-row",docFooterEditMetaRow:"theme-doc-footer-edit-meta-row",docSidebarContainer:"theme-doc-sidebar-container",docSidebarMenu:"theme-doc-sidebar-menu",docSidebarItemCategory:"theme-doc-sidebar-item-category",docSidebarItemLink:"theme-doc-sidebar-item-link",docSidebarItemCategoryLevel:e=>`theme-doc-sidebar-item-category-level-${e}`,docSidebarItemLinkLevel:e=>`theme-doc-sidebar-item-link-level-${e}`},blog:{blogFooterTagsRow:"theme-blog-footer-tags-row",blogFooterEditMetaRow:"theme-blog-footer-edit-meta-row"},pages:{pageFooterEditMetaRow:"theme-pages-footer-edit-meta-row"}}},7564(e,t,n){"use strict";n(4912)},7654(e,t,n){"use strict";n.d(t,{Oj:()=>u,Pq:()=>c,U4:()=>s});var r=n(7810),o=n(4848);const a={id:"T11",variations:{A:(0,o.jsxs)(o.Fragment,{children:["The easiest way to ",(0,o.jsx)("b",{children:"store"})," and ",(0,o.jsx)("b",{children:"sync"})," Data inside of your App"]}),B:(0,o.jsxs)(o.Fragment,{children:["The local-first ",(0,o.jsx)("b",{children:"Database"})," for ",(0,o.jsx)("b",{children:"JavaScript"})," Applications"]}),C:(0,o.jsxs)(o.Fragment,{children:["The Reactive Local-First ",(0,o.jsx)("b",{children:"Database"})," for Modern ",(0,o.jsx)("b",{children:"JavaScript"})," Apps"]}),D:(0,o.jsxs)(o.Fragment,{children:["The Local-First Database to ",(0,o.jsx)("b",{children:"Store"})," and ",(0,o.jsx)("b",{children:"Sync"})," App Data"]}),E:(0,o.jsxs)(o.Fragment,{children:["The Local-First ",(0,o.jsx)("b",{children:"Database"})," for ",(0,o.jsx)("b",{children:"JavaScript"})," Apps"]})}};let i;const l="test-group-"+a.id;function s(e="main"){if(i)return i;if("undefined"==typeof localStorage)return{variation:Object.keys(a.variations)[0],deviceType:"d",originId:e||""};const t=localStorage.getItem(l);return t?i=JSON.parse(t):(i={variation:(0,r.Oi)(Object.keys(a.variations)),deviceType:window.screen.width<=900?"m":"d",originId:e||""},localStorage.setItem(l,JSON.stringify(i))),console.log("currentTestGroup:"),console.dir(i),i}function c(){const e=s().variation;return a.variations[e]}function u(){if(localStorage.getItem(l)){const e=s();return["abt",a.id,"O:"+e.originId,"V:"+e.variation,"D:"+e.deviceType].join("_")}return!1}},7810(e,t,n){"use strict";function r(e){return e[e.length-1]}function o(e){return e[Math.floor(Math.random()*e.length)]}function a(e){return Array.isArray(e)?e.slice(0):[e]}function i(e){return Array.isArray(e)}function l(e,t){var n=0,r=-1;for(var o of e){if(!t(o,r+=1))break;n+=1}return n}function s(e){return e.filter(function(e,t,n){return n.indexOf(e)===t})}n.d(t,{$r:()=>a,Oi:()=>o,Sd:()=>l,dG:()=>r,jw:()=>s,k_:()=>i})},8120(e,t,n){"use strict";n.d(t,{A:()=>p});var r=n(6540),o=n(4164),a=n(6347),i=n(4586),l=n(6588),s=n(2303),c=n(689),u=n.n(c);function d(){const e=(0,a.zy)(),t=(0,a.W6)(),{siteConfig:{baseUrl:n}}=(0,i.A)(),[o,l]=(0,r.useState)({wordToHighlight:"",isTitleSuggestion:!1,titleText:""});return(0,r.useEffect)(()=>{if(!e.state?.highlightState||0===e.state.highlightState.wordToHighlight.length)return;l(e.state.highlightState);const{highlightState:n,...r}=e.state;t.replace({...e,state:r})},[e.state?.highlightState,t,e]),(0,r.useEffect)(()=>{if(0===o.wordToHighlight.length)return;const e=document.getElementsByTagName("article")[0]??document.getElementsByTagName("main")[0];if(!e)return;const t=new(u())(e),n={ignoreJoiners:!0};return t.mark(o.wordToHighlight,n),()=>t.unmark(n)},[o,n]),null}var f=n(4848);const p=e=>{const t=(0,r.useRef)(!1),c=(0,r.useRef)(null),u=(0,a.W6)(),{siteConfig:p={}}=(0,i.A)(),h=(p.plugins||[]).find(e=>Array.isArray(e)&&"string"==typeof e[0]&&e[0].includes("docusaurus-lunr-search")),m=(0,s.A)(),{baseUrl:g}=p,b=h&&h[1]?.assetUrl||g,v=(0,l.P_)("docusaurus-lunr-search"),y=()=>{t.current||(t.current=!0,Promise.all([fetch(`${b}${v.fileNames.searchDoc}`).then(e=>e.json()),fetch(`${b}${v.fileNames.lunrIndex}`).then(e=>e.json()),Promise.all([n.e(4250),n.e(7443)]).then(n.bind(n,4004)),Promise.all([n.e(1869),n.e(9187)]).then(n.bind(n,9187))]).then(([e,t,{default:n}])=>{const{searchDocs:r,options:o}=e;r&&0!==r.length&&(((e,t,n,r)=>{new n({searchDocs:e,searchIndex:t,baseUrl:g,inputSelector:"#search_input_react",autocompleteOptions:{hint:!1,appendTo:".navbar__search"},handleSelected:(e,t,n)=>{const o=n.url||"/";document.createElement("a").href=o,e.setVal(""),t.target.blur();let a="";if(r.highlightResult)try{const e=(n.text||n.subcategory||n.title).match(new RegExp("\\w*","g"));if(e&&e.length>0){const t=document.createElement("div");t.innerHTML=e[0],a=t.textContent}}catch(i){console.log(i)}u.push(o,{highlightState:{wordToHighlight:a}})},maxHits:r.maxHits})})(r,t,n,o),setIndexReady(!0))}))},x=(0,r.useCallback)(t=>{c.current.contains(t.target)||c.current.focus(),e.handleSearchBarToggle&&e.handleSearchBarToggle(!e.isSearchBarExpanded)},[e.isSearchBarExpanded]);let w;return m&&(w=window.navigator.platform.startsWith("Mac")?"Search \u2318+K":"Search Ctrl+K"),(0,f.jsxs)("div",{className:"navbar__search",children:[(0,f.jsx)("span",{"aria-label":"expand searchbar",role:"button",className:(0,o.A)("search-icon",{"search-icon-hidden":e.isSearchBarExpanded}),onClick:x,onKeyDown:x,tabIndex:0}),(0,f.jsx)("input",{id:"search_input_react",type:"search",placeholder:w,"aria-label":"Search",className:(0,o.A)("navbar__search-input",{"search-bar-expanded":e.isSearchBarExpanded},{"search-bar":!e.isSearchBarExpanded}),onClick:y,onMouseOver:y,onFocus:x,onBlur:x,ref:c,style:{marginLeft:"auto",marginRight:"auto",display:"block"}}),(0,f.jsx)(d,{})]},"search-box")}},8141(e,t,n){"use strict";n.d(t,{V:()=>s,c:()=>l});var r=n(6540),o=n(7654),a=n(8193),i=n(4848);function l(e,t,n=5,r=!1){if(!a.A.canUseDOM)return;const i="event_count_",l=localStorage.getItem(i+e),s=l?parseInt(l,10):0;if(!(s>=n)){if(localStorage.setItem(i+e,s+1+""),console.log("triggerTrackingEvent("+e+", "+t+", primary="+r+" "+s+"/"+n+")"),r&&"function"==typeof window.rdt)try{window.rdt("track","Lead",{transactionId:e+"-"+(new Date).getTime(),currency:"EUR",value:t})}catch(c){console.log("# Error on reddit trigger:"),console.dir(c)}if("function"==typeof window.gtag)try{window.gtag("event",e,{value:t,currency:"EUR"});const n=(0,o.Oj)();n&&window.gtag("event",n+"_"+e,{value:0,currency:"EUR"})}catch(c){console.log("# Error on google trigger:"),console.dir(c)}}}function s(e){return(0,r.useEffect)(()=>{a.A.canUseDOM&&l(e.type,e.value,e.maxPerUser,e.primary)},[]),(0,i.jsx)(i.Fragment,{})}},8168(e,t,n){"use strict";function r(){return r=Object.assign?Object.assign.bind():function(e){for(var t=1;tr})},8193(e,t,n){"use strict";n.d(t,{A:()=>o});const r="undefined"!=typeof window&&"document"in window&&"createElement"in window.document,o={canUseDOM:r,canUseEventListeners:r&&("addEventListener"in window||"attachEvent"in window),canUseIntersectionObserver:r&&"IntersectionObserver"in window,canUseViewport:r&&"screen"in window}},8295(e,t,n){"use strict";n.d(t,{zK:()=>p,vT:()=>u,Gy:()=>s,HW:()=>h,ht:()=>c,r7:()=>f,jh:()=>d});var r=n(6347),o=n(6588);const a=e=>e.versions.find(e=>e.isLast);function i(e,t){const n=function(e,t){return[...e.versions].sort((e,t)=>e.path===t.path?0:e.path.includes(t.path)?-1:t.path.includes(e.path)?1:0).find(e=>!!(0,r.B6)(t,{path:e.path,exact:!1,strict:!1}))}(e,t),o=n?.docs.find(e=>!!(0,r.B6)(t,{path:e.path,exact:!0,strict:!1}));return{activeVersion:n,activeDoc:o,alternateDocVersions:o?function(t){const n={};return e.versions.forEach(e=>{e.docs.forEach(r=>{r.id===t&&(n[e.name]=r)})}),n}(o.id):{}}}const l={},s=()=>(0,o.kh)("docusaurus-plugin-content-docs")??l,c=e=>{try{return(0,o.P_)("docusaurus-plugin-content-docs",e,{failfast:!0})}catch(t){throw new Error("You are using a feature of the Docusaurus docs plugin, but this plugin does not seem to be enabled"+("Default"===e?"":` (pluginId=${e}`),{cause:t})}};function u(e={}){const t=s(),{pathname:n}=(0,r.zy)();return function(e,t,n={}){const o=Object.entries(e).sort((e,t)=>t[1].path.localeCompare(e[1].path)).find(([,e])=>!!(0,r.B6)(t,{path:e.path,exact:!1,strict:!1})),a=o?{pluginId:o[0],pluginData:o[1]}:void 0;if(!a&&n.failfast)throw new Error(`Can't find active docs plugin for "${t}" pathname, while it was expected to be found. Maybe you tried to use a docs feature that can only be used on a docs-related page? Existing docs plugin paths are: ${Object.values(e).map(e=>e.path).join(", ")}`);return a}(t,n,e)}function d(e){return c(e).versions}function f(e){const t=c(e);return a(t)}function p(e){const t=c(e),{pathname:n}=(0,r.zy)();return i(t,n)}function h(e){const t=c(e),{pathname:n}=(0,r.zy)();return function(e,t){const n=a(e);return{latestDocSuggestion:i(e,t).alternateDocVersions[n.name],latestVersionSuggestion:n}}(t,n)}},8328(e,t,n){"use strict";n.d(t,{A:()=>f});n(6540);var r=n(3259),o=n.n(r),a=n(4054);const i={"0027230a":[()=>n.e(8382).then(n.bind(n,3520)),"@site/docs/rx-storage-lokijs.md",3520],"01684a0a":[()=>n.e(6616).then(n.bind(n,7971)),"@site/docs/rx-storage-memory-synced.md",7971],"01a106d5":[()=>n.e(5423).then(n.bind(n,7450)),"@site/docs/react.md",7450],"03e37916":[()=>n.e(6465).then(n.bind(n,5522)),"@site/docs/transactions-conflicts-revisions.md",5522],"045bd6f5":[()=>n.e(4475).then(n.bind(n,9978)),"@site/docs/encryption.md",9978],"0596642b":[()=>n.e(7537).then(n.bind(n,7196)),"@site/src/pages/meeting-paid-starter.tsx",7196],"08ff000c":[()=>n.e(3916).then(n.bind(n,9918)),"@site/docs/releases/16.0.0.md",9918],"0b761dc7":[()=>n.e(2835).then(n.bind(n,2796)),"@site/src/pages/sem/localstorage-database.tsx",2796],"0e268d20":[()=>n.e(2584).then(n.bind(n,9561)),"@site/src/pages/premium.tsx",9561],"0e467ee2":[()=>n.e(4989).then(n.bind(n,3318)),"@site/docs/articles/ideas.md",3318],"0e945c41":[()=>n.e(9796).then(n.bind(n,8908)),"@site/docs/replication-server.md",8908],"0f6e10f0":[()=>n.e(1475).then(n.bind(n,7706)),"@site/docs/articles/progressive-web-app-database.md",7706],"118cde4c":[()=>n.e(5272).then(n.bind(n,2318)),"@site/docs/replication-couchdb.md",2318],"11d75f9a":[()=>n.e(8897).then(n.bind(n,8282)),"@site/docs/articles/react-native-encryption.md",8282],"13dc6548":[()=>n.e(3483).then(n.bind(n,4637)),"@site/src/pages/sem/indexeddb-database.tsx",4637],"14d72841":[()=>n.e(9772).then(n.bind(n,5221)),"@site/src/pages/newsletter.tsx",5221],"15f1e21f":[()=>n.e(970).then(n.bind(n,7990)),"@site/src/pages/sem/vue-database.tsx",7990],17896441:[()=>Promise.all([n.e(1869),n.e(5943),n.e(8401)]).then(n.bind(n,9519)),"@theme/DocItem",9519],"187b985e":[()=>n.e(6866).then(n.bind(n,8243)),"@site/docs/replication-webrtc.md",8243],"1b0f8c91":[()=>n.e(3129).then(n.bind(n,5787)),"@site/docs/backup.md",5787],"1b238727":[()=>n.e(4013).then(n.bind(n,2493)),"@site/docs/rx-storage-performance.md",2493],"1b5fa8ad":[()=>n.e(9111).then(n.bind(n,5347)),"@site/src/pages/sem/nedb-alternative.tsx",5347],"1c0701dd":[()=>n.e(6953).then(n.bind(n,9328)),"@site/docs/middleware.md",9328],"1da545ff":[()=>n.e(9743).then(n.bind(n,135)),"@site/docs/releases/9.0.0.md",135],"1db64337":[()=>n.e(8413).then(n.bind(n,6471)),"@site/docs/overview.md",6471],"1df93b7f":[()=>Promise.resolve().then(n.bind(n,544)),"@site/src/pages/index.tsx",544],"1e0353aa":[()=>n.e(8588).then(n.bind(n,2219)),"@site/docs/rx-storage.md",2219],"1f391b9e":[()=>Promise.all([n.e(1869),n.e(5943),n.e(6061)]).then(n.bind(n,7973)),"@theme/MDXPage",7973],"21fa2740":[()=>n.e(5694).then(n.bind(n,8351)),"@site/docs/releases/15.0.0.md",8351],"22dd74f7":[()=>n.e(1567).then(n.t.bind(n,5226,19)),"@generated/docusaurus-plugin-content-docs/default/p/index-466.json",5226],"2456d5e0":[()=>n.e(2966).then(n.bind(n,7615)),"@site/docs/rx-database.md",7615],"25626d15":[()=>n.e(268).then(n.bind(n,7258)),"@site/src/pages/chat.tsx",7258],"2564bf4f":[()=>n.e(6724).then(n.bind(n,8365)),"@site/docs/rxdb-tradeoffs.md",8365],"25a43fd4":[()=>n.e(4812).then(n.bind(n,8803)),"@site/docs/rx-storage-shared-worker.md",8803],"26b8a621":[()=>n.e(2055).then(n.bind(n,1673)),"@site/docs/replication-p2p.md",1673],"280a2389":[()=>n.e(176).then(n.bind(n,522)),"@site/src/pages/meeting.tsx",522],"294ac9d5":[()=>n.e(8744).then(n.bind(n,2606)),"@site/docs/plugins.md",2606],"2aec6989":[()=>n.e(7498).then(n.bind(n,4633)),"@site/docs/articles/indexeddb-max-storage-limit.md",4633],"2c41656d":[()=>n.e(4142).then(n.bind(n,9376)),"@site/docs/articles/react-indexeddb.md",9376],"2efd0200":[()=>n.e(4132).then(n.bind(n,5179)),"@site/docs/tutorials/typescript.md",5179],"2fe9ecb2":[()=>n.e(5101).then(n.bind(n,8662)),"@site/docs/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.md",8662],"32667c41":[()=>n.e(8191).then(n.bind(n,9955)),"@site/docs/nosql-performance-tips.md",9955],"326aca46":[()=>n.e(4853).then(n.bind(n,1748)),"@site/docs/offline-first.md",1748],"3417a9c7":[()=>n.e(5350).then(n.bind(n,4572)),"@site/docs/articles/firestore-alternative.md",4572],"34f94d1b":[()=>n.e(5832).then(n.bind(n,8274)),"@site/docs/electron-database.md",8274],"35e4d287":[()=>n.e(7853).then(n.bind(n,383)),"@site/docs/releases/17.0.0.md",383],36715375:[()=>n.e(2076).then(n.bind(n,2323)),"@site/src/pages/code.tsx",2323],37055470:[()=>n.e(4970).then(n.bind(n,9528)),"@site/src/pages/sem/reddit.tsx",9528],"380cc66a":[()=>n.e(2061).then(n.bind(n,12)),"@site/docs/rx-storage-dexie.md",12],"38a45a95":[()=>n.e(405).then(n.bind(n,4806)),"@site/src/pages/sem/electron-database.tsx",4806],"38bbf12a":[()=>n.e(2078).then(n.bind(n,8251)),"@site/docs/articles/data-base.md",8251],"393be207":[()=>n.e(4134).then(n.bind(n,591)),"@site/src/pages/markdown-page.md",591],"39600c95":[()=>n.e(3148).then(n.bind(n,8228)),"@site/docs/rx-query.md",8228],"3ebfb37f":[()=>n.e(205).then(n.bind(n,6700)),"@site/src/pages/sem/angular-database.tsx",6700],"401008a8":[()=>n.e(5219).then(n.bind(n,3156)),"@site/docs/nodejs-database.md",3156],"40b6398a":[()=>n.e(9594).then(n.bind(n,6375)),"@site/docs/replication-supabase.md",6375],"41f941a1":[()=>n.e(5966).then(n.bind(n,2447)),"@site/docs/leader-election.md",2447],"432b83f9":[()=>n.e(4424).then(n.bind(n,4033)),"@site/docs/releases/8.0.0.md",4033],"4616b86a":[()=>n.e(465).then(n.bind(n,3844)),"@site/docs/rx-storage-mongodb.md",3844],"4777fd9a":[()=>n.e(6386).then(n.bind(n,692)),"@site/docs/alternatives.md",692],"4adf80bb":[()=>n.e(9548).then(n.bind(n,7014)),"@site/docs/rx-storage-pouchdb.md",7014],"4af60d2e":[()=>n.e(8545).then(n.bind(n,6136)),"@site/docs/articles/realtime-database.md",6136],"4ba7e5a3":[()=>n.e(9591).then(n.bind(n,5380)),"@site/docs/contribute.md",5380],"4ed9495b":[()=>n.e(1199).then(n.bind(n,5356)),"@site/docs/rx-storage-sqlite.md",5356],"4f17bbdd":[()=>n.e(2373).then(n.bind(n,1206)),"@site/src/pages/meeting-paid.tsx",1206],"502d8946":[()=>n.e(7817).then(n.bind(n,732)),"@site/src/pages/legal-notice.tsx",732],"51014a8a":[()=>n.e(9460).then(n.bind(n,9777)),"@site/docs/articles/websockets-sse-polling-webrtc-webtransport.md",9777],51038524:[()=>n.e(4889).then(n.bind(n,952)),"@site/docs/rx-storage-memory-mapped.md",952],51334108:[()=>n.e(8926).then(n.bind(n,6996)),"@site/docs/articles/mobile-database.md",6996],"5134b15f":[()=>n.e(10).then(n.bind(n,4066)),"@site/src/pages/sem/capacitor-database.tsx",4066],"55a5b596":[()=>n.e(6717).then(n.bind(n,4557)),"@site/docs/rx-schema.md",4557],58215328:[()=>n.e(5353).then(n.bind(n,960)),"@site/src/pages/sem/ionic-database.tsx",960],"597d88be":[()=>n.e(8715).then(n.bind(n,4190)),"@site/docs/articles/firebase-realtime-database-alternative.md",4190],"5a273530":[()=>n.e(3881).then(n.bind(n,4944)),"@site/docs/rx-storage-remote.md",4944],"5b5afcec":[()=>n.e(3633).then(n.bind(n,5255)),"@site/docs/articles/json-based-database.md",5255],"5e95c892":[()=>n.e(9647).then(n.bind(n,7121)),"@theme/DocsRoot",7121],"5e9f5e1a":[()=>Promise.resolve().then(n.bind(n,4784)),"@generated/docusaurus.config",4784],61792630:[()=>n.e(7277).then(n.bind(n,5270)),"@site/docs/articles/angular-indexeddb.md",5270],"6187b59a":[()=>n.e(5866).then(n.bind(n,5640)),"@site/docs/rx-storage-worker.md",5640],"68a466be":[()=>n.e(3997).then(n.bind(n,3963)),"@site/docs/downsides-of-offline-first.md",3963],"6ae3580c":[()=>n.e(5320).then(n.bind(n,2325)),"@site/docs/replication.md",2325],"6bfb0089":[()=>n.e(6422).then(n.bind(n,6235)),"@site/docs/adapters.md",6235],"6cbff7c2":[()=>n.e(7408).then(n.bind(n,7010)),"@site/docs/rx-storage-opfs.md",7010],"6fa8aa1a":[()=>n.e(1264).then(n.bind(n,5379)),"@site/src/pages/sem/pouchdb-alternative.tsx",5379],"6fd28feb":[()=>n.e(4618).then(n.bind(n,5989)),"@site/docs/rx-storage-foundationdb.md",5989],"714575d7":[()=>n.e(3185).then(n.bind(n,924)),"@site/docs/rx-local-document.md",924],"77979bef":[()=>n.e(5861).then(n.bind(n,4113)),"@site/docs/articles/local-first-future.md",4113],"77d975e6":[()=>n.e(3949).then(n.bind(n,6805)),"@site/docs/articles/in-memory-nosql-database.md",6805],"7815dd0c":[()=>n.e(5335).then(n.bind(n,2015)),"@site/docs/population.md",2015],"7bbb96fd":[()=>n.e(3495).then(n.bind(n,8435)),"@site/src/pages/service-submitted.tsx",8435],"7f02c700":[()=>n.e(9592).then(n.bind(n,5589)),"@site/docs/replication-firestore.md",5589],"8070e160":[()=>n.e(3822).then(n.bind(n,4143)),"@site/docs/quickstart.md",4143],"8084fe3b":[()=>n.e(9515).then(n.bind(n,7458)),"@site/docs/articles/vue-indexeddb.md",7458],"820807a1":[()=>n.e(7836).then(n.bind(n,8906)),"@site/docs/articles/local-database.md",8906],"8288c265":[()=>n.e(9881).then(n.bind(n,2309)),"@site/docs/releases/11.0.0.md",2309],"8489a755":[()=>n.e(7700).then(n.bind(n,7883)),"@site/docs/articles/jquery-database.md",7883],"84a3af36":[()=>n.e(6861).then(n.bind(n,7916)),"@site/docs/articles/json-database.md",7916],"84ae55a4":[()=>n.e(588).then(n.bind(n,7465)),"@site/docs/replication-nats.md",7465],"85caacef":[()=>n.e(3015).then(n.bind(n,8496)),"@site/src/pages/sem/expo-database.tsx",8496],"86b4e356":[()=>n.e(2786).then(n.bind(n,2344)),"@site/docs/orm.md",2344],"8a22f3a9":[()=>n.e(9257).then(n.bind(n,2159)),"@site/docs/errors.md",2159],"8a442806":[()=>n.e(1054).then(n.bind(n,9770)),"@site/docs/fulltext-search.md",9770],"8aa53ed7":[()=>n.e(6723).then(n.bind(n,7841)),"@site/docs/articles/angular-database.md",7841],"8b0a0922":[()=>n.e(4557).then(n.bind(n,9463)),"@site/docs/slow-indexeddb.md",9463],"8b4bf532":[()=>n.e(4027).then(n.bind(n,7507)),"@site/src/pages/sem/nosql-database.tsx",7507],"8bc07e20":[()=>n.e(1850).then(n.bind(n,3628)),"@site/docs/capacitor-database.md",3628],"8bc82b1f":[()=>n.e(9997).then(n.bind(n,2591)),"@site/docs/rx-pipeline.md",2591],"8bfd920a":[()=>n.e(2631).then(n.bind(n,9678)),"@site/docs/articles/ionic-storage.md",9678],"90102fdf":[()=>n.e(2633).then(n.bind(n,9935)),"@site/src/pages/sem/nodejs-database.tsx",9935],"91b454ee":[()=>n.e(4202).then(n.bind(n,449)),"@site/docs/rx-storage-sharding.md",449],"924d6dd6":[()=>n.e(5122).then(n.bind(n,7665)),"@site/docs/electron.md",7665],"92698a99":[()=>n.e(4166).then(n.bind(n,7379)),"@site/docs/rx-storage-indexeddb.md",7379],"931f4566":[()=>n.e(3595).then(n.bind(n,8288)),"@site/docs/schema-validation.md",8288],"951d0efb":[()=>n.e(6115).then(n.bind(n,5160)),"@site/docs/replication-mongodb.md",5160],98405524:[()=>n.e(5504).then(n.bind(n,1934)),"@site/src/pages/survey.tsx",1934],"9dae6e71":[()=>n.e(3325).then(n.bind(n,2231)),"@site/src/pages/sem/firestore-alternative.tsx",2231],"9dd8ea89":[()=>n.e(1715).then(n.bind(n,7775)),"@site/docs/logger.md",7775],"9e91b6f0":[()=>n.e(3021).then(n.bind(n,5200)),"@site/docs/why-nosql.md",5200],a406dc27:[()=>n.e(1500).then(n.bind(n,6837)),"@site/docs/migration-storage.md",6837],a442adcd:[()=>n.e(8760).then(n.bind(n,5551)),"@site/docs/reactivity.md",5551],a574e172:[()=>n.e(7149).then(n.bind(n,5384)),"@site/docs/replication-http.md",5384],a69eebfc:[()=>n.e(9408).then(n.bind(n,5878)),"@site/docs/query-optimizer.md",5878],a7456010:[()=>n.e(1235).then(n.t.bind(n,8552,19)),"@generated/docusaurus-plugin-content-pages/default/__plugin.json",8552],a7bd4aaa:[()=>n.e(7098).then(n.bind(n,1723)),"@theme/DocVersionRoot",1723],a7f10198:[()=>n.e(1098).then(n.bind(n,1337)),"@site/docs/data-migration.md",1337],a94703ab:[()=>Promise.all([n.e(1869),n.e(9048)]).then(n.bind(n,7510)),"@theme/DocRoot",7510],aa14e6b1:[()=>n.e(9824).then(n.bind(n,717)),"@site/docs/replication-graphql.md",717],ab919a1f:[()=>n.e(6491).then(n.bind(n,30)),"@site/docs/articles/embedded-database.md",30],aba21aa0:[()=>n.e(5742).then(n.t.bind(n,7093,19)),"@generated/docusaurus-plugin-content-docs/default/__plugin.json",7093],ac62b32d:[()=>n.e(9192).then(n.bind(n,3228)),"@site/docs/replication-websocket.md",3228],ad16b3ea:[()=>n.e(8674).then(n.bind(n,5045)),"@site/docs/dev-mode.md",5045],ae2c2832:[()=>n.e(3321).then(n.bind(n,6839)),"@site/src/pages/sem/svelte-database.tsx",6839],b0889a22:[()=>n.e(1107).then(n.bind(n,3519)),"@site/docs/cleanup.md",3519],b2653a00:[()=>n.e(3588).then(n.bind(n,4968)),"@site/docs/articles/vue-database.md",4968],b30f4f1f:[()=>n.e(3779).then(n.bind(n,8305)),"@site/docs/rx-attachment.md",8305],b672caf7:[()=>n.e(813).then(n.bind(n,9264)),"@site/docs/articles/javascript-vector-database.md",9264],b8c49ce4:[()=>n.e(6355).then(n.bind(n,4971)),"@site/docs/releases/13.0.0.md",4971],badcd764:[()=>n.e(8318).then(n.bind(n,3922)),"@site/docs/articles/flutter-database.md",3922],bdd39edd:[()=>n.e(5852).then(n.bind(n,1541)),"@site/src/pages/sem/browser-database.tsx",1541],c0f75fb9:[()=>n.e(833).then(n.bind(n,6440)),"@site/docs/articles/zero-latency-local-first.md",6440],c3bc9c50:[()=>n.e(9167).then(n.bind(n,8097)),"@site/docs/articles/react-database.md",8097],c44853e1:[()=>n.e(6584).then(n.bind(n,4321)),"@site/docs/rx-state.md",4321],c4de80f8:[()=>n.e(2777).then(n.bind(n,2186)),"@site/docs/install.md",2186],c6349bb6:[()=>n.e(5740).then(n.bind(n,7813)),"@site/docs/releases/14.0.0.md",7813],c6fdd490:[()=>n.e(4141).then(n.bind(n,7291)),"@site/docs/rx-server-scaling.md",7291],c7b47308:[()=>n.e(7793).then(n.bind(n,2007)),"@site/src/pages/sem/gads.tsx",2007],c843a053:[()=>n.e(9146).then(n.bind(n,3596)),"@site/docs/third-party-plugins.md",3596],c9c8e0b6:[()=>n.e(7249).then(n.bind(n,4715)),"@site/docs/articles/ionic-database.md",4715],cbbe8f0a:[()=>n.e(3852).then(n.bind(n,4706)),"@site/docs/rx-collection.md",4706],cde77f4f:[()=>n.e(6287).then(n.bind(n,6465)),"@site/src/pages/premium-submitted.tsx",6465],d04a108d:[()=>n.e(2853).then(n.bind(n,4801)),"@site/docs/replication-appwrite.md",4801],d20e74b4:[()=>n.e(5123).then(n.bind(n,6510)),"@site/docs/crdt.md",6510],d2758528:[()=>n.e(2586).then(n.bind(n,60)),"@site/docs/articles/browser-storage.md",60],d4da9db3:[()=>n.e(1400).then(n.bind(n,9319)),"@site/docs/rx-storage-memory.md",9319],d622bd51:[()=>n.e(2845).then(n.bind(n,4787)),"@site/docs/migration-schema.md",4787],db34d6b0:[()=>n.e(7320).then(n.bind(n,9891)),"@site/src/pages/license.tsx",9891],dbde2ffe:[()=>n.e(6543).then(n.bind(n,4506)),"@site/docs/rx-document.md",4506],dc42ba65:[()=>n.e(4962).then(n.bind(n,1918)),"@site/docs/key-compression.md",1918],de9a3c97:[()=>n.e(4194).then(n.bind(n,1450)),"@site/docs/articles/reactjs-storage.md",1450],e24529eb:[()=>n.e(6797).then(n.bind(n,2473)),"@site/docs/rx-storage-localstorage-meta-optimizer.md",2473],e6b4453d:[()=>n.e(2360).then(n.bind(n,6137)),"@site/docs/query-cache.md",6137],e8a836f3:[()=>n.e(5265).then(n.bind(n,9349)),"@site/src/pages/sem/react-native-database.tsx",9349],eadd9b3c:[()=>n.e(4886).then(n.bind(n,4304)),"@site/docs/rx-storage-filesystem-node.md",4304],eb2e4b0c:[()=>n.e(3988).then(n.bind(n,8642)),"@site/docs/rx-storage-localstorage.md",8642],ebace26e:[()=>n.e(4028).then(n.bind(n,1948)),"@site/docs/releases/10.0.0.md",1948],ec526260:[()=>n.e(7396).then(n.bind(n,894)),"@site/docs/articles/browser-database.md",894],ed2d6610:[()=>n.e(3469).then(n.bind(n,3883)),"@site/docs/releases/12.0.0.md",3883],ee1b9f21:[()=>n.e(6998).then(n.bind(n,1708)),"@site/docs/react-native-database.md",1708],f14ec96f:[()=>n.e(7513).then(n.bind(n,2468)),"@site/docs/articles/offline-database.md",2468],f15938da:[()=>n.e(4630).then(n.bind(n,7560)),"@site/docs/articles/localstorage.md",7560],f1c185f0:[()=>n.e(8907).then(n.bind(n,6e3)),"@site/src/pages/sem/indexeddb-database-2.tsx",6e3],f43e80a8:[()=>n.e(1558).then(n.bind(n,6849)),"@site/docs/rx-server.md",6849],f44bb875:[()=>n.e(561).then(n.bind(n,8610)),"@site/docs/articles/frontend-database.md",8610],f490b64c:[()=>n.e(8845).then(n.bind(n,1393)),"@site/src/pages/sem/react-database.tsx",1393],f61fdf57:[()=>n.e(5448).then(n.bind(n,624)),"@site/docs/articles/optimistic-ui.md",624],fe2a63b2:[()=>n.e(1018).then(n.bind(n,4324)),"@site/src/pages/consulting.tsx",4324],fe7a07ee:[()=>n.e(2085).then(n.bind(n,734)),"@site/docs/rx-storage-denokv.md",734],feac4174:[()=>n.e(4920).then(n.bind(n,1249)),"@site/src/pages/demo-submitted.tsx",1249],ff492cda:[()=>n.e(7722).then(n.bind(n,1382)),"@site/src/pages/sem/watermelondb-alternative.tsx",1382]};var l=n(4848);function s({error:e,retry:t,pastDelay:n}){return e?(0,l.jsxs)("div",{style:{textAlign:"center",color:"#fff",backgroundColor:"#fa383e",borderColor:"#fa383e",borderStyle:"solid",borderRadius:"0.25rem",borderWidth:"1px",boxSizing:"border-box",display:"block",padding:"1rem",flex:"0 0 50%",marginLeft:"25%",marginRight:"25%",marginTop:"5rem",maxWidth:"50%",width:"100%"},children:[(0,l.jsx)("p",{children:String(e)}),(0,l.jsx)("div",{children:(0,l.jsx)("button",{type:"button",onClick:t,children:"Retry"})})]}):n?(0,l.jsx)("div",{style:{display:"flex",justifyContent:"center",alignItems:"center",height:"100vh"},children:(0,l.jsx)("svg",{id:"loader",style:{width:128,height:110,position:"absolute",top:"calc(100vh - 64%)"},viewBox:"0 0 45 45",xmlns:"http://www.w3.org/2000/svg",stroke:"#61dafb",children:(0,l.jsxs)("g",{fill:"none",fillRule:"evenodd",transform:"translate(1 1)",strokeWidth:"2",children:[(0,l.jsxs)("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0",children:[(0,l.jsx)("animate",{attributeName:"r",begin:"1.5s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-opacity",begin:"1.5s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-width",begin:"1.5s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})]}),(0,l.jsxs)("circle",{cx:"22",cy:"22",r:"6",strokeOpacity:"0",children:[(0,l.jsx)("animate",{attributeName:"r",begin:"3s",dur:"3s",values:"6;22",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-opacity",begin:"3s",dur:"3s",values:"1;0",calcMode:"linear",repeatCount:"indefinite"}),(0,l.jsx)("animate",{attributeName:"stroke-width",begin:"3s",dur:"3s",values:"2;0",calcMode:"linear",repeatCount:"indefinite"})]}),(0,l.jsx)("circle",{cx:"22",cy:"22",r:"8",children:(0,l.jsx)("animate",{attributeName:"r",begin:"0s",dur:"1.5s",values:"6;1;2;3;4;5;6",calcMode:"linear",repeatCount:"indefinite"})})]})})}):null}var c=n(6921),u=n(3102);function d(e,t){if("*"===e)return o()({loading:s,loader:()=>n.e(9293).then(n.bind(n,9293)),modules:["@theme/NotFound"],webpack:()=>[9293],render(e,t){const n=e.default;return(0,l.jsx)(u.W,{value:{plugin:{name:"native",id:"default"}},children:(0,l.jsx)(n,{...t})})}});const r=a[`${e}-${t}`],d={},f=[],p=[],h=(0,c.A)(r);return Object.entries(h).forEach(([e,t])=>{const n=i[t];n&&(d[e]=n[0],f.push(n[1]),p.push(n[2]))}),o().Map({loading:s,loader:d,modules:f,webpack:()=>p,render(t,n){const o=JSON.parse(JSON.stringify(r));Object.entries(t).forEach(([t,n])=>{const r=n.default;if(!r)throw new Error(`The page component at ${e} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`);"object"!=typeof r&&"function"!=typeof r||Object.keys(n).filter(e=>"default"!==e).forEach(e=>{r[e]=n[e]});let a=o;const i=t.split(".");i.slice(0,-1).forEach(e=>{a=a[e]}),a[i[i.length-1]]=r});const a=o.__comp;delete o.__comp;const i=o.__context;delete o.__context;const s=o.__props;return delete o.__props,(0,l.jsx)(u.W,{value:i,children:(0,l.jsx)(a,{...o,...s,...n})})}})}const f=[{path:"/chat",component:d("/chat","4b9"),exact:!0},{path:"/code",component:d("/code","309"),exact:!0},{path:"/consulting",component:d("/consulting","c8b"),exact:!0},{path:"/demo-submitted",component:d("/demo-submitted","618"),exact:!0},{path:"/legal-notice",component:d("/legal-notice","b0b"),exact:!0},{path:"/license",component:d("/license","bdc"),exact:!0},{path:"/markdown-page",component:d("/markdown-page","3d7"),exact:!0},{path:"/meeting",component:d("/meeting","b8a"),exact:!0},{path:"/meeting-paid",component:d("/meeting-paid","372"),exact:!0},{path:"/meeting-paid-starter",component:d("/meeting-paid-starter","666"),exact:!0},{path:"/newsletter",component:d("/newsletter","c98"),exact:!0},{path:"/premium",component:d("/premium","40c"),exact:!0},{path:"/premium-submitted",component:d("/premium-submitted","c98"),exact:!0},{path:"/sem/angular-database",component:d("/sem/angular-database","4ab"),exact:!0},{path:"/sem/browser-database",component:d("/sem/browser-database","baf"),exact:!0},{path:"/sem/capacitor-database",component:d("/sem/capacitor-database","e71"),exact:!0},{path:"/sem/electron-database",component:d("/sem/electron-database","7ac"),exact:!0},{path:"/sem/expo-database",component:d("/sem/expo-database","f40"),exact:!0},{path:"/sem/firestore-alternative",component:d("/sem/firestore-alternative","113"),exact:!0},{path:"/sem/gads",component:d("/sem/gads","bb5"),exact:!0},{path:"/sem/indexeddb-database",component:d("/sem/indexeddb-database","b06"),exact:!0},{path:"/sem/indexeddb-database-2",component:d("/sem/indexeddb-database-2","e14"),exact:!0},{path:"/sem/ionic-database",component:d("/sem/ionic-database","102"),exact:!0},{path:"/sem/localstorage-database",component:d("/sem/localstorage-database","169"),exact:!0},{path:"/sem/nedb-alternative",component:d("/sem/nedb-alternative","f4a"),exact:!0},{path:"/sem/nodejs-database",component:d("/sem/nodejs-database","b04"),exact:!0},{path:"/sem/nosql-database",component:d("/sem/nosql-database","6b7"),exact:!0},{path:"/sem/pouchdb-alternative",component:d("/sem/pouchdb-alternative","b71"),exact:!0},{path:"/sem/react-database",component:d("/sem/react-database","ab6"),exact:!0},{path:"/sem/react-native-database",component:d("/sem/react-native-database","425"),exact:!0},{path:"/sem/reddit",component:d("/sem/reddit","4da"),exact:!0},{path:"/sem/svelte-database",component:d("/sem/svelte-database","fdd"),exact:!0},{path:"/sem/vue-database",component:d("/sem/vue-database","dfc"),exact:!0},{path:"/sem/watermelondb-alternative",component:d("/sem/watermelondb-alternative","d56"),exact:!0},{path:"/service-submitted",component:d("/service-submitted","3ce"),exact:!0},{path:"/survey",component:d("/survey","bf5"),exact:!0},{path:"/",component:d("/","e5f"),exact:!0},{path:"/",component:d("/","2b0"),routes:[{path:"/",component:d("/","a99"),routes:[{path:"/",component:d("/","f22"),routes:[{path:"/adapters.html",component:d("/adapters.html","d79"),exact:!0},{path:"/alternatives.html",component:d("/alternatives.html","180"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/angular-database.html",component:d("/articles/angular-database.html","7ba"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/angular-indexeddb.html",component:d("/articles/angular-indexeddb.html","bc4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/browser-database.html",component:d("/articles/browser-database.html","822"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/browser-storage.html",component:d("/articles/browser-storage.html","fc0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/data-base.html",component:d("/articles/data-base.html","fc4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/embedded-database.html",component:d("/articles/embedded-database.html","6c0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/firebase-realtime-database-alternative.html",component:d("/articles/firebase-realtime-database-alternative.html","82f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/firestore-alternative.html",component:d("/articles/firestore-alternative.html","f23"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/flutter-database.html",component:d("/articles/flutter-database.html","e70"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/frontend-database.html",component:d("/articles/frontend-database.html","b9e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/ideas",component:d("/articles/ideas","8cc"),exact:!0},{path:"/articles/in-memory-nosql-database.html",component:d("/articles/in-memory-nosql-database.html","94c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/indexeddb-max-storage-limit.html",component:d("/articles/indexeddb-max-storage-limit.html","52c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/ionic-database.html",component:d("/articles/ionic-database.html","fcf"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/ionic-storage.html",component:d("/articles/ionic-storage.html","5ca"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/javascript-vector-database.html",component:d("/articles/javascript-vector-database.html","c04"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/jquery-database.html",component:d("/articles/jquery-database.html","21c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/json-based-database.html",component:d("/articles/json-based-database.html","457"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/json-database.html",component:d("/articles/json-database.html","cd0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/local-database.html",component:d("/articles/local-database.html","389"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/local-first-future.html",component:d("/articles/local-first-future.html","215"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html",component:d("/articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.html","fa3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/localstorage.html",component:d("/articles/localstorage.html","589"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/mobile-database.html",component:d("/articles/mobile-database.html","2e2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/offline-database.html",component:d("/articles/offline-database.html","020"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/optimistic-ui.html",component:d("/articles/optimistic-ui.html","12a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/progressive-web-app-database.html",component:d("/articles/progressive-web-app-database.html","a95"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/react-database.html",component:d("/articles/react-database.html","0ae"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/react-indexeddb.html",component:d("/articles/react-indexeddb.html","e83"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/react-native-encryption.html",component:d("/articles/react-native-encryption.html","44b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/reactjs-storage.html",component:d("/articles/reactjs-storage.html","046"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/realtime-database.html",component:d("/articles/realtime-database.html","e61"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/vue-database.html",component:d("/articles/vue-database.html","6aa"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/vue-indexeddb.html",component:d("/articles/vue-indexeddb.html","66e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/websockets-sse-polling-webrtc-webtransport.html",component:d("/articles/websockets-sse-polling-webrtc-webtransport.html","66d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/articles/zero-latency-local-first.html",component:d("/articles/zero-latency-local-first.html","c0c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/backup.html",component:d("/backup.html","51d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/capacitor-database.html",component:d("/capacitor-database.html","149"),exact:!0,sidebar:"tutorialSidebar"},{path:"/cleanup.html",component:d("/cleanup.html","b7d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/contribution.html",component:d("/contribution.html","faf"),exact:!0,sidebar:"tutorialSidebar"},{path:"/crdt.html",component:d("/crdt.html","c2c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/data-migration.html",component:d("/data-migration.html","bf6"),exact:!0},{path:"/dev-mode.html",component:d("/dev-mode.html","e56"),exact:!0,sidebar:"tutorialSidebar"},{path:"/downsides-of-offline-first.html",component:d("/downsides-of-offline-first.html","c17"),exact:!0,sidebar:"tutorialSidebar"},{path:"/electron-database.html",component:d("/electron-database.html","024"),exact:!0,sidebar:"tutorialSidebar"},{path:"/electron.html",component:d("/electron.html","237"),exact:!0,sidebar:"tutorialSidebar"},{path:"/encryption.html",component:d("/encryption.html","132"),exact:!0,sidebar:"tutorialSidebar"},{path:"/errors.html",component:d("/errors.html","559"),exact:!0,sidebar:"tutorialSidebar"},{path:"/fulltext-search.html",component:d("/fulltext-search.html","ada"),exact:!0,sidebar:"tutorialSidebar"},{path:"/install.html",component:d("/install.html","3c3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/key-compression.html",component:d("/key-compression.html","85e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/leader-election.html",component:d("/leader-election.html","9f4"),exact:!0,sidebar:"tutorialSidebar"},{path:"/logger.html",component:d("/logger.html","4f5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/middleware.html",component:d("/middleware.html","4f8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/migration-schema.html",component:d("/migration-schema.html","6c3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/migration-storage.html",component:d("/migration-storage.html","aa6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/nodejs-database.html",component:d("/nodejs-database.html","21d"),exact:!0,sidebar:"tutorialSidebar"},{path:"/nosql-performance-tips.html",component:d("/nosql-performance-tips.html","174"),exact:!0,sidebar:"tutorialSidebar"},{path:"/offline-first.html",component:d("/offline-first.html","0c3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/orm.html",component:d("/orm.html","1fc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/overview.html",component:d("/overview.html","22a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/plugins.html",component:d("/plugins.html","373"),exact:!0,sidebar:"tutorialSidebar"},{path:"/population.html",component:d("/population.html","7f7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/query-cache.html",component:d("/query-cache.html","4d9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/query-optimizer.html",component:d("/query-optimizer.html","772"),exact:!0,sidebar:"tutorialSidebar"},{path:"/quickstart.html",component:d("/quickstart.html","21b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/react-native-database.html",component:d("/react-native-database.html","f66"),exact:!0,sidebar:"tutorialSidebar"},{path:"/react.html",component:d("/react.html","589"),exact:!0,sidebar:"tutorialSidebar"},{path:"/reactivity.html",component:d("/reactivity.html","6bf"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/10.0.0.html",component:d("/releases/10.0.0.html","869"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/11.0.0.html",component:d("/releases/11.0.0.html","665"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/12.0.0.html",component:d("/releases/12.0.0.html","8d8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/13.0.0.html",component:d("/releases/13.0.0.html","05c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/14.0.0.html",component:d("/releases/14.0.0.html","72c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/15.0.0.html",component:d("/releases/15.0.0.html","5e9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/16.0.0.html",component:d("/releases/16.0.0.html","7b5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/17.0.0.html",component:d("/releases/17.0.0.html","29c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/8.0.0.html",component:d("/releases/8.0.0.html","c17"),exact:!0,sidebar:"tutorialSidebar"},{path:"/releases/9.0.0.html",component:d("/releases/9.0.0.html","e32"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-appwrite.html",component:d("/replication-appwrite.html","123"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-couchdb.html",component:d("/replication-couchdb.html","af3"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-firestore.html",component:d("/replication-firestore.html","fe7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-graphql.html",component:d("/replication-graphql.html","733"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-http.html",component:d("/replication-http.html","ada"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-mongodb.html",component:d("/replication-mongodb.html","2f9"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-nats.html",component:d("/replication-nats.html","753"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-p2p.html",component:d("/replication-p2p.html","e43"),exact:!0},{path:"/replication-server.html",component:d("/replication-server.html","22a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-supabase.html",component:d("/replication-supabase.html","8c2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-webrtc.html",component:d("/replication-webrtc.html","1f5"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication-websocket.html",component:d("/replication-websocket.html","20c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/replication.html",component:d("/replication.html","860"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-attachment.html",component:d("/rx-attachment.html","88f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-collection.html",component:d("/rx-collection.html","83c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-database.html",component:d("/rx-database.html","788"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-document.html",component:d("/rx-document.html","4cc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-local-document.html",component:d("/rx-local-document.html","34a"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-pipeline.html",component:d("/rx-pipeline.html","143"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-query.html",component:d("/rx-query.html","60e"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-schema.html",component:d("/rx-schema.html","5c7"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-server-scaling.html",component:d("/rx-server-scaling.html","960"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-server.html",component:d("/rx-server.html","1dc"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-state.html",component:d("/rx-state.html","928"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-denokv.html",component:d("/rx-storage-denokv.html","e15"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-dexie.html",component:d("/rx-storage-dexie.html","ed6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-filesystem-node.html",component:d("/rx-storage-filesystem-node.html","ff0"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-foundationdb.html",component:d("/rx-storage-foundationdb.html","0ee"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-indexeddb.html",component:d("/rx-storage-indexeddb.html","823"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-localstorage-meta-optimizer.html",component:d("/rx-storage-localstorage-meta-optimizer.html","817"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-localstorage.html",component:d("/rx-storage-localstorage.html","01f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-lokijs.html",component:d("/rx-storage-lokijs.html","96a"),exact:!0},{path:"/rx-storage-memory-mapped.html",component:d("/rx-storage-memory-mapped.html","e84"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-memory-synced.html",component:d("/rx-storage-memory-synced.html","7b4"),exact:!0},{path:"/rx-storage-memory.html",component:d("/rx-storage-memory.html","723"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-mongodb.html",component:d("/rx-storage-mongodb.html","e65"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-opfs.html",component:d("/rx-storage-opfs.html","5cb"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-performance.html",component:d("/rx-storage-performance.html","cab"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-pouchdb.html",component:d("/rx-storage-pouchdb.html","a91"),exact:!0},{path:"/rx-storage-remote.html",component:d("/rx-storage-remote.html","03c"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-sharding.html",component:d("/rx-storage-sharding.html","0e6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-shared-worker.html",component:d("/rx-storage-shared-worker.html","8f8"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-sqlite.html",component:d("/rx-storage-sqlite.html","9e6"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage-worker.html",component:d("/rx-storage-worker.html","762"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rx-storage.html",component:d("/rx-storage.html","ee2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/rxdb-tradeoffs.html",component:d("/rxdb-tradeoffs.html","970"),exact:!0},{path:"/schema-validation.html",component:d("/schema-validation.html","c8b"),exact:!0,sidebar:"tutorialSidebar"},{path:"/slow-indexeddb.html",component:d("/slow-indexeddb.html","e17"),exact:!0,sidebar:"tutorialSidebar"},{path:"/third-party-plugins.html",component:d("/third-party-plugins.html","49f"),exact:!0,sidebar:"tutorialSidebar"},{path:"/transactions-conflicts-revisions.html",component:d("/transactions-conflicts-revisions.html","282"),exact:!0,sidebar:"tutorialSidebar"},{path:"/tutorials/typescript.html",component:d("/tutorials/typescript.html","1c2"),exact:!0,sidebar:"tutorialSidebar"},{path:"/why-nosql.html",component:d("/why-nosql.html","7cb"),exact:!0,sidebar:"tutorialSidebar"}]}]}]},{path:"*",component:d("*")}]},8426(e,t){function n(e){let t,n=[];for(let r of e.split(",").map(e=>e.trim()))if(/^-?\d+$/.test(r))n.push(parseInt(r,10));else if(t=r.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/)){let[e,r,o,a]=t;if(r&&a){r=parseInt(r),a=parseInt(a);const e=rr})},8601(e,t,n){"use strict";n.d(t,{A:()=>j,h:()=>E});var r=n(6540),o=n(4164),a=(n(8426),n(9532)),i=n(4848);const l={js:{start:"\\/\\/",end:""},jsBlock:{start:"\\/\\*",end:"\\*\\/"},jsx:{start:"\\{\\s*\\/\\*",end:"\\*\\/\\s*\\}"},bash:{start:"#",end:""},html:{start:"\x3c!--",end:"--\x3e"}};Object.keys(l);const s=(0,r.createContext)(null);function c({metadata:e,wordWrap:t,children:n}){const o=(0,r.useMemo)(()=>({metadata:e,wordWrap:t}),[e,t]);return(0,i.jsx)(s.Provider,{value:o,children:n})}function u(){const e=(0,r.useContext)(s);if(null===e)throw new a.dV("CodeBlockContextProvider");return e}var d=n(1312);function f({className:e,...t}){return(0,i.jsx)("button",{type:"button",...t,className:(0,o.A)("clean-btn",e)})}function p(e){return(0,i.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,i.jsx)("path",{fill:"currentColor",d:"M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"})})}function h(e){return(0,i.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,i.jsx)("path",{fill:"currentColor",d:"M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"})})}const m={copyButtonCopied:"copyButtonCopied_Vdqa",copyButtonIcons:"copyButtonIcons_IEyt",copyButtonIcon:"copyButtonIcon_TrPX",copyButtonSuccessIcon:"copyButtonSuccessIcon_cVMy"};function g(e){return e?(0,d.T)({id:"theme.CodeBlock.copied",message:"Copied",description:"The copied button label on code blocks"}):(0,d.T)({id:"theme.CodeBlock.copyButtonAriaLabel",message:"Copy code to clipboard",description:"The ARIA label for copy code blocks button"})}function b({className:e}){const{copyCode:t,isCopied:n}=function(){const{metadata:{code:e}}=u(),[t,n]=(0,r.useState)(!1),o=(0,r.useRef)(void 0),a=(0,r.useCallback)(()=>{navigator.clipboard.writeText(e).then(()=>{n(!0),o.current=window.setTimeout(()=>{n(!1)},1e3)})},[e]);return(0,r.useEffect)(()=>()=>window.clearTimeout(o.current),[]),{copyCode:a,isCopied:t}}();return(0,i.jsx)(f,{"aria-label":g(n),title:(0,d.T)({id:"theme.CodeBlock.copy",message:"Copy",description:"The copy button label on code blocks"}),className:(0,o.A)(e,m.copyButton,n&&m.copyButtonCopied),onClick:t,children:(0,i.jsxs)("span",{className:m.copyButtonIcons,"aria-hidden":"true",children:[(0,i.jsx)(p,{className:m.copyButtonIcon}),(0,i.jsx)(h,{className:m.copyButtonSuccessIcon})]})})}function v(e){return(0,i.jsx)("svg",{viewBox:"0 0 24 24",...e,children:(0,i.jsx)("path",{fill:"currentColor",d:"M4 19h6v-2H4v2zM20 5H4v2h16V5zm-3 6H4v2h13.25c1.1 0 2 .9 2 2s-.9 2-2 2H15v-2l-3 3l3 3v-2h2c2.21 0 4-1.79 4-4s-1.79-4-4-4z"})})}const y="wordWrapButtonIcon_b1P5",x="wordWrapButtonEnabled_uzNF";function w({className:e}){const{wordWrap:t}=u();if(!(t.isEnabled||t.isCodeScrollable))return!1;const n=(0,d.T)({id:"theme.CodeBlock.wordWrapToggle",message:"Toggle word wrap",description:"The title attribute for toggle word wrapping button of code block lines"});return(0,i.jsx)(f,{onClick:()=>t.toggle(),className:(0,o.A)(e,t.isEnabled&&x),"aria-label":n,title:n,children:(0,i.jsx)(v,{className:y,"aria-hidden":"true"})})}const k="buttonGroup_M5ko",S="codeBlockContainer_Ckt0",C={position:"relative","--prism-background-color":"var(--bg-color-code)","--prism-color":"var(--shiki-foreground)"};function E({children:e}){const t=(0,r.useRef)(null),[n,a]=(0,r.useState)(""),l=function(){const[e,t]=(0,r.useState)(!1),[n,o]=(0,r.useState)(!1),a=(0,r.useRef)(null),i=(0,r.useCallback)(()=>{const n=a.current?.querySelector("code");n&&(e?(n.style.whiteSpace="",n.style.overflowWrap=""):(n.style.whiteSpace="pre-wrap",n.style.overflowWrap="anywhere"),t(e=>!e))},[e]),l=(0,r.useCallback)(()=>{if(!a.current)return;const{scrollWidth:e,clientWidth:t}=a.current;o(e>t)},[]);return(0,r.useEffect)(()=>{l()},[e,l]),(0,r.useEffect)(()=>(window.addEventListener("resize",l,{passive:!0}),()=>window.removeEventListener("resize",l)),[l]),{codeBlockRef:a,isEnabled:e,isCodeScrollable:n,toggle:i}}();(0,r.useEffect)(()=>{t.current&&a(t.current.textContent||"")},[e]);const s={codeInput:n,code:n,className:"language-text",language:"text",title:void 0,lineNumbersStart:void 0,lineClassNames:{}};if("string"==typeof e)return(0,i.jsx)(c,{metadata:{...s,code:e,codeInput:e},wordWrap:l,children:(0,i.jsxs)("div",{className:(0,o.A)(S,"theme-code-block"),style:C,children:[(0,i.jsx)("pre",{ref:l.codeBlockRef,children:(0,i.jsx)("code",{children:e})}),(0,i.jsxs)("div",{className:k,children:[(0,i.jsx)(w,{}),(0,i.jsx)(b,{})]})]})});if(r.isValidElement(e)&&"pre"===e.type){const n=e.props,r=n.className||"",a={...s,className:r||"language-text",language:r.replace("language-","")||"text"};return(0,i.jsx)(c,{metadata:a,wordWrap:l,children:(0,i.jsxs)("div",{className:(0,o.A)(S,"theme-code-block"),style:C,children:[(0,i.jsx)("pre",{...n,ref:e=>{t.current=e,l.codeBlockRef&&(l.codeBlockRef.current=e)}}),(0,i.jsxs)("div",{className:k,children:[(0,i.jsx)(w,{}),(0,i.jsx)(b,{})]})]})})}return e}function j(e){return(0,i.jsx)(E,{children:(0,i.jsx)("pre",{...e})})}},8711(e,t,n){"use strict";n.d(t,{A:()=>St});var r=n(6540),o=n(4164),a=n(7489),i=n(5500),l=n(6347),s=n(1312),c=n(5062),u=n(4848);const d="__docusaurus_skipToContent_fallback";function f(e){e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")}function p(){const e=(0,r.useRef)(null),{action:t}=(0,l.W6)(),n=(0,r.useCallback)(e=>{e.preventDefault();const t=document.querySelector("main:first-of-type")??document.getElementById(d);t&&f(t)},[]);return(0,c.$)(({location:n})=>{e.current&&!n.hash&&"PUSH"===t&&f(e.current)}),{containerRef:e,onClick:n}}const h=(0,s.T)({id:"theme.common.skipToMainContent",description:"The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation",message:"Skip to main content"});function m(e){const t=e.children??h,{containerRef:n,onClick:r}=p();return(0,u.jsx)("div",{ref:n,role:"region","aria-label":h,children:(0,u.jsx)("a",{...e,href:`#${d}`,onClick:r,children:t})})}var g=n(7559),b=n(4090);const v="skipToContent_fXgn";function y(){return(0,u.jsx)(m,{className:v})}var x=n(6342),w=n(5041),k=n(2949);function S(e){return(0,u.jsx)(k.g,{clickable:!0})}const C="closeButton_CVFx";function E(e){return(0,u.jsx)("button",{type:"button","aria-label":(0,s.T)({id:"theme.AnnouncementBar.closeButtonAriaLabel",message:"Close",description:"The ARIA label for close button of announcement bar"}),...e,className:(0,o.A)("clean-btn close",C,e.className),children:(0,u.jsx)(S,{width:14,height:14,strokeWidth:3.1})})}const j="content_knG7";function _(e){const{announcementBar:t}=(0,x.p)(),{content:n}=t;return(0,u.jsx)("div",{...e,className:(0,o.A)(j,e.className),dangerouslySetInnerHTML:{__html:n}})}const O="announcementBar_mb4j",T="announcementBarPlaceholder_vyr4",P="announcementBarClose_gvF7",A="announcementBarContent_xLdY";function N(){const{announcementBar:e}=(0,x.p)(),{isActive:t,close:n}=(0,w.M)();if(!t)return null;const{backgroundColor:r,textColor:a,isCloseable:i}=e;return(0,u.jsxs)("div",{className:(0,o.A)(g.G.announcementBar.container,O),style:{backgroundColor:r,color:a},role:"banner",children:[i&&(0,u.jsx)("div",{className:T}),(0,u.jsx)(_,{className:A}),i&&(0,u.jsx)(E,{onClick:n,className:P})]})}var M=n(2069),L=n(3104);var I=n(9532),R=n(5600);const $=r.createContext(null);function F({children:e}){const t=function(){const e=(0,M.M)(),t=(0,R.YL)(),[n,o]=(0,r.useState)(!1),a=null!==t.component,i=(0,I.ZC)(a);return(0,r.useEffect)(()=>{a&&!i&&o(!0)},[a,i]),(0,r.useEffect)(()=>{a?e.shown||o(!0):o(!1)},[e.shown,a]),(0,r.useMemo)(()=>[n,o],[n])}();return(0,u.jsx)($.Provider,{value:t,children:e})}function z(e){if(e.component){const t=e.component;return(0,u.jsx)(t,{...e.props})}}function D(){const e=(0,r.useContext)($);if(!e)throw new I.dV("NavbarSecondaryMenuDisplayProvider");const[t,n]=e,o=(0,r.useCallback)(()=>n(!1),[n]),a=(0,R.YL)();return(0,r.useMemo)(()=>({shown:t,hide:o,content:z(a)}),[o,a,t])}function B(e){if(!r.version)throw new Error("version is missing");return parseInt(r.version.split(".")[0],10)<19?{inert:e?"":void 0}:{inert:e}}function H({children:e,inert:t}){return(0,u.jsx)("div",{className:(0,o.A)(g.G.layout.navbar.mobileSidebar.panel,"navbar-sidebar__item menu"),...B(t),children:e})}function V({header:e,primaryMenu:t,secondaryMenu:n}){const{shown:r}=D();return(0,u.jsxs)("div",{className:(0,o.A)(g.G.layout.navbar.mobileSidebar.container,"navbar-sidebar"),children:[e,(0,u.jsxs)("div",{className:(0,o.A)("navbar-sidebar__items",{"navbar-sidebar__items--show-secondary":r}),children:[(0,u.jsx)(H,{inert:r,children:t}),(0,u.jsx)(H,{inert:!r,children:n})]})]})}var U=n(5293),q=n(2303);function W(e){return(0,u.jsx)("svg",{viewBox:"0 0 24 24",width:24,height:24,...e,children:(0,u.jsx)("path",{fill:"currentColor",d:"M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"})})}function G(e){return(0,u.jsx)("svg",{viewBox:"0 0 24 24",width:24,height:24,...e,children:(0,u.jsx)("path",{fill:"currentColor",d:"M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"})})}function X(e){return(0,u.jsx)("svg",{viewBox:"0 0 24 24",width:24,height:24,...e,children:(0,u.jsx)("path",{fill:"currentColor",d:"m12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9zm4.95-13.95c1.313 1.313 2.05 3.093 2.05 4.95s-0.738 3.637-2.05 4.95c-1.313 1.313-3.093 2.05-4.95 2.05v-14c1.857 0 3.637 0.737 4.95 2.05z"})})}const K="toggle_vylO",Y="toggleButton_gllP",Q="toggleIcon_g3eP",Z="systemToggleIcon_QzmC",J="lightToggleIcon_pyhR",ee="darkToggleIcon_wfgR",te="toggleButtonDisabled_aARS";function ne(e){switch(e){case null:return(0,s.T)({message:"system mode",id:"theme.colorToggle.ariaLabel.mode.system",description:"The name for the system color mode"});case"light":return(0,s.T)({message:"light mode",id:"theme.colorToggle.ariaLabel.mode.light",description:"The name for the light color mode"});case"dark":return(0,s.T)({message:"dark mode",id:"theme.colorToggle.ariaLabel.mode.dark",description:"The name for the dark color mode"});default:throw new Error(`unexpected color mode ${e}`)}}function re(e){return(0,s.T)({message:"Switch between dark and light mode (currently {mode})",id:"theme.colorToggle.ariaLabel",description:"The ARIA label for the color mode toggle"},{mode:ne(e)})}function oe(){return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(W,{"aria-hidden":!0,className:(0,o.A)(Q,J)}),(0,u.jsx)(G,{"aria-hidden":!0,className:(0,o.A)(Q,ee)}),(0,u.jsx)(X,{"aria-hidden":!0,className:(0,o.A)(Q,Z)})]})}function ae({className:e,buttonClassName:t,respectPrefersColorScheme:n,value:r,onChange:a}){const i=(0,q.A)();return(0,u.jsx)("div",{className:(0,o.A)(K,e),children:(0,u.jsx)("button",{className:(0,o.A)("clean-btn",Y,!i&&te,t),type:"button",onClick:()=>a(function(e,t){if(!t)return"dark"===e?"light":"dark";switch(e){case null:return"light";case"light":return"dark";case"dark":return null;default:throw new Error(`unexpected color mode ${e}`)}}(r,n)),disabled:!i,title:ne(r),"aria-label":re(r),children:(0,u.jsx)(oe,{})})})}const ie=r.memo(ae),le="darkNavbarColorModeToggle_Q0Zn";function se({className:e}){const t=(0,x.p)().navbar.style,{disableSwitch:n,respectPrefersColorScheme:r}=(0,x.p)().colorMode,{colorModeChoice:o,setColorMode:a}=(0,U.G)();return n?null:(0,u.jsx)(ie,{className:e,buttonClassName:"dark"===t?le:void 0,respectPrefersColorScheme:r,value:o,onChange:a})}var ce=n(9529);function ue(){return(0,u.jsx)(ce.A,{className:"navbar__brand",imageClassName:"navbar__logo",titleClassName:"navbar__title text--truncate"})}function de(){const e=(0,M.M)();return(0,u.jsx)("button",{type:"button","aria-label":(0,s.T)({id:"theme.docs.sidebar.closeSidebarButtonAriaLabel",message:"Close navigation bar",description:"The ARIA label for close button of mobile sidebar"}),className:"clean-btn navbar-sidebar__close",onClick:()=>e.toggle(),children:(0,u.jsx)(S,{color:"var(--ifm-color-emphasis-600)"})})}function fe(){return(0,u.jsxs)("div",{className:"navbar-sidebar__brand",children:[(0,u.jsx)(ue,{}),(0,u.jsx)(se,{className:"margin-right--md"}),(0,u.jsx)(de,{})]})}var pe=n(8774),he=n(6025),me=n(6654);function ge(e,t){return void 0!==e&&void 0!==t&&new RegExp(e,"gi").test(t)}var be=n(3186),ve=n(8141),ye=n(3103);function xe({activeBasePath:e,activeBaseRegex:t,to:n,href:o,label:a,html:i,isDropdownLink:l,prependBaseUrlToHref:s,...c}){const d=(0,he.Ay)(n),f=(0,he.Ay)(e),p=(0,he.Ay)(o,{forcePrependBaseUrl:!0}),h=a&&o&&!(0,me.A)(o),m=i?{dangerouslySetInnerHTML:{__html:i}}:{children:(0,u.jsxs)(u.Fragment,{children:[a,h&&(0,u.jsx)(be.A,{...l&&{width:12,height:12}})]})},g=o?(0,u.jsx)(pe.A,{onClick:()=>{(0,ve.c)("navbar_click",.1),(0,ve.c)("navbar_click_"+a.toLowerCase(),.2)},href:s?p:o,...c,...m}):(0,u.jsx)(pe.A,{to:d,isNavLink:!0,...(e||t)&&{isActive:(e,n)=>t?ge(t,n.pathname):n.pathname.startsWith(f)},...c,...m});if(c.dropdown){const[e,t]=(0,r.useState)(!1),n=(0,r.useRef)(null),o=()=>{n.current&&clearTimeout(n.current),t(!0)},a=()=>{n.current=setTimeout(()=>{t(!1)},200)};return(0,u.jsxs)("div",{className:"dropdown-wrapper",onMouseEnter:o,onMouseLeave:a,children:[g,e&&(0,u.jsx)(ye.KJ,{which:c.dropdown})]})}return g}function we({className:e,isDropdownItem:t,...n}){return(0,u.jsx)("li",{className:"menu__list-item",children:(0,u.jsx)(xe,{className:(0,o.A)("menu__link",e),...n})})}function ke({className:e,isDropdownItem:t=!1,...n}){const r=(0,u.jsx)(xe,{className:(0,o.A)(t?"dropdown__link":"navbar__item navbar__link",e),isDropdownLink:t,...n});return t?(0,u.jsx)("li",{children:r}):r}function Se({mobile:e=!1,position:t,...n}){const r=e?we:ke;return(0,u.jsx)(r,{...n,activeClassName:n.activeClassName??(e?"menu__link--active":"navbar__link--active")})}var Ce=n(1422),Ee=n(9169),je=n(4586);const _e="dropdownNavbarItemMobile_J0Sd";function Oe(e,t){return e.some(e=>function(e,t){return!!(0,Ee.ys)(e.to,t)||!!ge(e.activeBaseRegex,t)||!(!e.activeBasePath||!t.startsWith(e.activeBasePath))}(e,t))}function Te({collapsed:e,onClick:t}){return(0,u.jsx)("button",{"aria-label":e?(0,s.T)({id:"theme.navbar.mobileDropdown.collapseButton.expandAriaLabel",message:"Expand the dropdown",description:"The ARIA label of the button to expand the mobile dropdown navbar item"}):(0,s.T)({id:"theme.navbar.mobileDropdown.collapseButton.collapseAriaLabel",message:"Collapse the dropdown",description:"The ARIA label of the button to collapse the mobile dropdown navbar item"}),"aria-expanded":!e,type:"button",className:"clean-btn menu__caret",onClick:t})}function Pe({items:e,className:t,position:n,onClick:a,...i}){const s=function(){const{siteConfig:{baseUrl:e}}=(0,je.A)(),{pathname:t}=(0,l.zy)();return t.replace(e,"/")}(),c=(0,Ee.ys)(i.to,s),d=Oe(e,s),{collapsed:f,toggleCollapsed:p}=function({active:e}){const{collapsed:t,toggleCollapsed:n,setCollapsed:o}=(0,Ce.u)({initialState:()=>!e});return(0,r.useEffect)(()=>{e&&o(!1)},[e,o]),{collapsed:t,toggleCollapsed:n}}({active:c||d}),h=i.to?void 0:"#";return(0,u.jsxs)("li",{className:(0,o.A)("menu__list-item",{"menu__list-item--collapsed":f}),children:[(0,u.jsxs)("div",{className:(0,o.A)("menu__list-item-collapsible",{"menu__list-item-collapsible--active":c}),children:[(0,u.jsx)(xe,{role:"button",className:(0,o.A)(_e,"menu__link menu__link--sublist",t),href:h,...i,onClick:e=>{"#"===h&&e.preventDefault(),p()},children:i.children??i.label}),(0,u.jsx)(Te,{collapsed:f,onClick:e=>{e.preventDefault(),p()}})]}),(0,u.jsx)(Ce.N,{lazy:!0,as:"ul",className:"menu__list",collapsed:f,children:e.map((e,t)=>(0,r.createElement)(Ge,{mobile:!0,isDropdownItem:!0,onClick:a,activeClassName:"menu__link--active",...e,key:t}))})]})}function Ae({items:e,position:t,className:n,onClick:a,...i}){const l=(0,r.useRef)(null),[s,c]=(0,r.useState)(!1);return(0,r.useEffect)(()=>{const e=e=>{l.current&&!l.current.contains(e.target)&&c(!1)};return document.addEventListener("mousedown",e),document.addEventListener("touchstart",e),document.addEventListener("focusin",e),()=>{document.removeEventListener("mousedown",e),document.removeEventListener("touchstart",e),document.removeEventListener("focusin",e)}},[l]),(0,u.jsxs)("div",{ref:l,className:(0,o.A)("navbar__item","dropdown","dropdown--hoverable",{"dropdown--right":"right"===t,"dropdown--show":s}),children:[(0,u.jsx)(xe,{"aria-haspopup":"true","aria-expanded":s,role:"button",href:i.to?void 0:"#",className:(0,o.A)("navbar__link",n),...i,onClick:i.to?void 0:e=>e.preventDefault(),onKeyDown:e=>{"Enter"===e.key&&(e.preventDefault(),c(!s))},children:i.children??i.label}),(0,u.jsx)("ul",{className:"dropdown__menu",children:e.map((e,t)=>(0,r.createElement)(Ge,{isDropdownItem:!0,activeClassName:"dropdown__link--active",...e,key:t}))})]})}function Ne({mobile:e=!1,...t}){const n=e?Pe:Ae;return(0,u.jsx)(n,{...t})}var Me=n(2131),Le=n(7485);function Ie({width:e=20,height:t=20,...n}){return(0,u.jsx)("svg",{viewBox:"0 0 24 24",width:e,height:t,"aria-hidden":!0,...n,children:(0,u.jsx)("path",{fill:"currentColor",d:"M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"})})}const Re="iconLanguage_nlXk";function $e(){const{siteConfig:e,i18n:{localeConfigs:t}}=(0,je.A)(),n=(0,Me.o)(),r=(0,Le.Hl)(e=>e.location.search),o=(0,Le.Hl)(e=>e.location.hash),a=e=>{const n=t[e];if(!n)throw new Error(`Docusaurus bug, no locale config found for locale=${e}`);return n};return{getURL:(t,i)=>{const l=(0,Le.jy)([r,i.queryString],"append");return`${(t=>a(t).url===e.url?`pathname://${n.createUrl({locale:t,fullyQualified:!1})}`:n.createUrl({locale:t,fullyQualified:!0}))(t)}${l}${o}`},getLabel:e=>a(e).label,getLang:e=>a(e).htmlLang}}var Fe=n(8120);const ze="navbarSearchContainer_dCNk";function De({children:e,className:t}){return(0,u.jsx)("div",{className:(0,o.A)(t,ze),children:e})}var Be=n(8295),He=n(4718);var Ve=n(3886);function Ue({docsPluginId:e,configs:t}){return function(e,t){if(t){const n=new Map(e.map(e=>[e.name,e])),r=(t,r)=>{const o=n.get(t);if(!o)throw new Error(`No docs version exist for name '${t}', please verify your 'docsVersionDropdown' navbar item versions config.\nAvailable version names:\n- ${e.map(e=>`${e.name}`).join("\n- ")}`);return{version:o,label:r?.label??o.label}};return Array.isArray(t)?t.map(e=>r(e,void 0)):Object.entries(t).map(([e,t])=>r(e,t))}return e.map(e=>({version:e,label:e.label}))}((0,Be.jh)(e),t)}function qe(e,t){return t.alternateDocVersions[e.name]??function(e){return e.docs.find(t=>t.id===e.mainDocId)}(e)}const We={default:Se,localeDropdown:function({mobile:e,dropdownItemsBefore:t,dropdownItemsAfter:n,queryString:r,...o}){const a=$e(),{i18n:{currentLocale:i,locales:l}}=(0,je.A)(),c=[...t,...l.map(t=>({label:a.getLabel(t),lang:a.getLang(t),to:a.getURL(t,{queryString:r}),target:"_self",autoAddBaseUrl:!1,className:t===i?e?"menu__link--active":"dropdown__link--active":""})),...n],d=e?(0,s.T)({message:"Languages",id:"theme.navbar.mobileLanguageDropdown.label",description:"The label for the mobile language switcher dropdown"}):a.getLabel(i);return(0,u.jsx)(Ne,{...o,mobile:e,label:(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(Ie,{className:Re}),d]}),items:c})},search:function({mobile:e,className:t}){return e?null:(0,u.jsx)(De,{className:t,children:(0,u.jsx)(Fe.A,{})})},dropdown:Ne,html:function({value:e,className:t,mobile:n=!1,isDropdownItem:r=!1}){const a=r?"li":"div";return(0,u.jsx)(a,{className:(0,o.A)({navbar__item:!n&&!r,"menu__list-item":n},t),dangerouslySetInnerHTML:{__html:e}})},doc:function({docId:e,label:t,docsPluginId:n,...r}){const{activeDoc:o}=(0,Be.zK)(n),a=(0,He.QB)(e,n),i=o?.path===a?.path;return null===a||a.unlisted&&!i?null:(0,u.jsx)(Se,{exact:!0,...r,isActive:()=>i||!!o?.sidebar&&o.sidebar===a.sidebar,label:t??a.id,to:a.path})},docSidebar:function({sidebarId:e,label:t,docsPluginId:n,...r}){const{activeDoc:o}=(0,Be.zK)(n),a=(0,He.fW)(e,n).link;if(!a)throw new Error(`DocSidebarNavbarItem: Sidebar with ID "${e}" doesn't have anything to be linked to.`);return(0,u.jsx)(Se,{exact:!0,...r,isActive:()=>o?.sidebar===e,label:t??a.label,to:a.path})},docsVersion:function({label:e,to:t,docsPluginId:n,...r}){const o=(0,He.Vd)(n)[0],a=e??o.label,i=t??(e=>e.docs.find(t=>t.id===e.mainDocId))(o).path;return(0,u.jsx)(Se,{...r,label:a,to:i})},docsVersionDropdown:function({mobile:e,docsPluginId:t,dropdownActiveClassDisabled:n,dropdownItemsBefore:r,dropdownItemsAfter:o,versions:a,...i}){const l=(0,Le.Hl)(e=>e.location.search),c=(0,Le.Hl)(e=>e.location.hash),d=(0,Be.zK)(t),{savePreferredVersionName:f}=(0,Ve.g1)(t),p=Ue({docsPluginId:t,configs:a}),h=function({docsPluginId:e,versionItems:t}){return(0,He.Vd)(e).map(e=>t.find(t=>t.version===e)).filter(e=>void 0!==e)[0]??t[0]}({docsPluginId:t,versionItems:p}),m=[...r,...p.map(function({version:e,label:t}){return{label:t,to:`${qe(e,d).path}${l}${c}`,isActive:()=>e===d.activeVersion,onClick:()=>f(e.name)}}),...o],g=e&&m.length>1?(0,s.T)({id:"theme.navbar.mobileVersionsDropdown.label",message:"Versions",description:"The label for the navbar versions dropdown on mobile view"}):h.label,b=e&&m.length>1?void 0:qe(h.version,d).path;return m.length<=1?(0,u.jsx)(Se,{...i,mobile:e,label:g,to:b,isActive:n?()=>!1:void 0}):(0,u.jsx)(Ne,{...i,mobile:e,label:g,to:b,items:m,isActive:n?()=>!1:void 0})}};function Ge({type:e,...t}){const n=function(e,t){return e&&"default"!==e?e:"items"in t?"dropdown":"default"}(e,t),r=We[n];if(!r)throw new Error(`No NavbarItem component found for type "${e}".`);return(0,u.jsx)(r,{...t})}function Xe(){const e=(0,M.M)(),t=(0,x.p)().navbar.items;return(0,u.jsx)("ul",{className:"menu__list",children:t.map((t,n)=>(0,r.createElement)(Ge,{mobile:!0,...t,onClick:()=>e.toggle(),key:n}))})}function Ke(e){return(0,u.jsx)("button",{...e,type:"button",className:"clean-btn navbar-sidebar__back",children:(0,u.jsx)(s.A,{id:"theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel",description:"The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)",children:"\u2190 Back to main menu"})})}function Ye(){const e=0===(0,x.p)().navbar.items.length,t=D();return(0,u.jsxs)(u.Fragment,{children:[!e&&(0,u.jsx)(Ke,{onClick:()=>t.hide()}),t.content]})}function Qe(){const e=(0,M.M)();return function(e=!0){(0,r.useEffect)(()=>(document.body.style.overflow=e?"hidden":"visible",()=>{document.body.style.overflow="visible"}),[e])}(e.shown),e.shouldRender?(0,u.jsx)(V,{header:(0,u.jsx)(fe,{}),primaryMenu:(0,u.jsx)(Xe,{}),secondaryMenu:(0,u.jsx)(Ye,{})}):null}const Ze="navbarHideable_jvwV",Je="navbarHidden_nLSi";function et(e){return(0,u.jsx)("div",{role:"presentation",...e,className:(0,o.A)("navbar-sidebar__backdrop",e.className)})}function tt({children:e}){const{navbar:{hideOnScroll:t,style:n}}=(0,x.p)(),a=(0,M.M)(),{navbarRef:i,isNavbarVisible:l}=function(e){const[t,n]=(0,r.useState)(e),o=(0,r.useRef)(!1),a=(0,r.useRef)(0),i=(0,r.useCallback)(e=>{null!==e&&(a.current=e.getBoundingClientRect().height)},[]);return(0,L.Mq)(({scrollY:t},r)=>{if(!e)return;if(t=i?n(!1):t+s{if(!e)return;const r=t.location.hash;if(r?document.getElementById(r.substring(1)):void 0)return o.current=!0,void n(!1);n(!0)}),{navbarRef:i,isNavbarVisible:t}}(t);return(0,u.jsxs)("nav",{ref:i,"aria-label":(0,s.T)({id:"theme.NavBar.navAriaLabel",message:"Main",description:"The ARIA label for the main navigation"}),className:(0,o.A)(g.G.layout.navbar.container,"navbar","navbar--fixed-top",t&&[Ze,!l&&Je],{"navbar--dark":"dark"===n,"navbar--primary":"primary"===n,"navbar-sidebar--show":a.shown}),children:[e,(0,u.jsx)(et,{onClick:a.toggle}),(0,u.jsx)(Qe,{})]})}var nt=n(440);const rt="errorBoundaryError_a6uf";function ot(e){return(0,u.jsx)("button",{type:"button",...e,children:(0,u.jsx)(s.A,{id:"theme.ErrorPageContent.tryAgain",description:"The label of the button to try again rendering when the React error boundary captures an error",children:"Try again"})})}function at({error:e}){const t=(0,nt.rA)(e).map(e=>e.message).join("\n\nCause:\n");return(0,u.jsx)("p",{className:rt,children:t})}class it extends r.Component{componentDidCatch(e,t){throw this.props.onError(e,t)}render(){return this.props.children}}function lt({width:e=30,height:t=30,className:n,...r}){return(0,u.jsx)("svg",{className:n,width:e,height:t,viewBox:"0 0 30 30","aria-hidden":"true",...r,children:(0,u.jsx)("path",{stroke:"currentColor",strokeLinecap:"round",strokeMiterlimit:"10",strokeWidth:"2",d:"M4 7h22M4 15h22M4 23h22"})})}function st(){const{toggle:e,shown:t}=(0,M.M)();return(0,u.jsx)("button",{onClick:e,"aria-label":(0,s.T)({id:"theme.docs.sidebar.toggleSidebarButtonAriaLabel",message:"Toggle navigation bar",description:"The ARIA label for hamburger menu button of mobile navigation"}),"aria-expanded":t,className:"navbar__toggle clean-btn",type:"button",children:(0,u.jsx)(lt,{})})}const ct="colorModeToggle_x44X";function ut({items:e}){return(0,u.jsx)(u.Fragment,{children:e.map((e,t)=>(0,u.jsx)(it,{onError:t=>new Error(`A theme navbar item failed to render.\nPlease double-check the following navbar item (themeConfig.navbar.items) of your Docusaurus config:\n${JSON.stringify(e,null,2)}`,{cause:t}),children:(0,u.jsx)(Ge,{...e})},t))})}function dt({left:e,right:t}){return(0,u.jsxs)("div",{className:"navbar__inner",children:[(0,u.jsx)("div",{className:(0,o.A)(g.G.layout.navbar.containerLeft,"navbar__items"),children:e}),(0,u.jsx)("div",{className:(0,o.A)(g.G.layout.navbar.containerRight,"navbar__items navbar__items--right"),children:t})]})}function ft(){const e=(0,M.M)(),t=(0,x.p)().navbar.items,[n,r]=function(e){function t(e){return"left"===(e.position??"right")}return[e.filter(t),e.filter(e=>!t(e))]}(t);return(0,u.jsx)(dt,{left:(0,u.jsxs)(u.Fragment,{children:[!e.disabled&&(0,u.jsx)(st,{}),(0,u.jsx)(ue,{}),(0,u.jsx)(ut,{items:n})]}),right:(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(ut,{items:r}),(0,u.jsx)(se,{className:ct})]})})}const pt={position:"fixed",left:0,zIndex:10,height:2,width:"100vw",backgroundColor:"var(--color-top)",borderTopRightRadius:2,borderBottomRightRadius:2,transformOrigin:"left center",willChange:"transform",backfaceVisibility:"hidden",contain:"layout"};function ht(){const[e,t]=(0,r.useState)(0);return(0,r.useEffect)(()=>{const e=()=>t(function(){const{scrollTop:e,scrollHeight:t,clientHeight:n}=document.documentElement,r=document.body.scrollTop,o=e+r,a=t-n;return a>0?o/a*100:0}());return window.addEventListener("scroll",e,{passive:!0}),e(),()=>window.removeEventListener("scroll",e)},[]),(0,u.jsxs)(tt,{children:[(0,u.jsx)(ft,{}),(0,u.jsx)("div",{className:"navbar-line",style:{...pt,transform:`scaleX(${Math.max(0,Math.min(1,e/100))})`}})]})}var mt=n(2676),gt=n(9514);function bt(){const e={navLinks:[{label:"Docs",href:"/overview.html"},{href:"/replication.html",label:"Sync"},{href:"/rx-storage.html",label:"Storages"},{label:"Premium",target:"_blank",href:"/premium/"},{label:"Support",target:"_blank",href:"/consulting/"}],communityLinks:[{label:"Newsletter",target:"_blank",href:"/newsletter/",logo:(0,u.jsx)(mt.N,{})},{label:"Twitter",href:"https://twitter.com/intent/user?screen_name=rxdbjs",target:"_blank",logo:"/files/icons/twitter-blue.svg"},{label:"LinkedIn",href:"https://www.linkedin.com/company/rxdb",target:"_blank",logo:(0,u.jsx)(gt.A,{})},{label:"Github",target:"_blank",href:"/code/",logo:(0,u.jsx)("span",{className:"navbar-icon-github",style:{width:22,height:22,display:"inline-block"}})},{label:"Discord",target:"_blank",href:"/chat/",logo:(0,u.jsx)("span",{className:"navbar-icon-discord",style:{width:22,height:22,display:"inline-block"}})}],policyLinks:[{label:"Our Customers",href:"/#reviews"},{label:"Become a Partner",href:"/consulting#become-partner"},{label:"Legal Notice",target:"_blank",href:"/legal-notice/"}]};return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(u.Fragment,{children:(0,u.jsx)("div",{className:"block footer dark",children:(0,u.jsxs)("div",{style:{display:"flex",flexWrap:"wrap",gap:44},className:"content",children:[(0,u.jsx)("div",{className:"half left",children:(0,u.jsxs)("span",{children:[(0,u.jsx)("a",{variant:"text",href:"/",className:"footer-logo-button",children:(0,u.jsx)("img",{src:"/files/logo/logo_text_white.svg",alt:"RxDB",loading:"lazy",className:"width-140-120"})}),(0,u.jsx)("div",{className:"footer-community-links",children:e.communityLinks.map((e,t)=>(0,u.jsx)("a",{variant:"text",href:e.href,target:e.target?"_blank":"","aria-label":e.label,children:"string"==typeof e.logo?(0,u.jsx)("img",{src:e.logo,alt:e.label,loading:"lazy"}):e.logo},e.href+t))})]})}),(0,u.jsx)("div",{className:"half right",style:{display:"flex",flex:1},children:(0,u.jsxs)("div",{style:{display:"flex"},children:[(0,u.jsx)("div",{className:"footer-links min-width-180-135",children:e.navLinks.map((e,t)=>(0,u.jsx)("a",{variant:"text",href:e.href,target:e.target?"_blank":"",children:e.label},e.href+t))}),(0,u.jsx)("div",{className:"footer-links min-width-180-135",children:e.policyLinks.map((e,t)=>(0,u.jsx)("a",{variant:"text",href:e.href,target:e.target?"_blank":"",children:e.label},e.href+t))})]})})]})})}),(0,u.jsx)("div",{className:"navbar-line",style:{display:"block",zIndex:10,height:1.5,backgroundColor:"var(--color-top)",borderTopRightRadius:2,borderBottomRightRadius:2,width:"100%"}})]})}const vt=(0,I.fM)([U.a,w.o,L.Tv,Ve.VQ,i.Jx,function({children:e}){return(0,u.jsx)(R.y_,{children:(0,u.jsx)(M.e,{children:(0,u.jsx)(F,{children:e})})})}]);function yt({children:e}){return(0,u.jsx)(vt,{children:e})}var xt=n(1107);function wt({error:e,tryAgain:t}){return(0,u.jsx)("main",{className:"container margin-vert--xl",children:(0,u.jsx)("div",{className:"row",children:(0,u.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,u.jsx)(xt.A,{as:"h1",className:"hero__title",children:(0,u.jsx)(s.A,{id:"theme.ErrorPageContent.title",description:"The title of the fallback page when the page crashed",children:"This page crashed."})}),(0,u.jsx)("div",{className:"margin-vert--lg",children:(0,u.jsx)(ot,{onClick:t,className:"button button--primary shadow--lw"})}),(0,u.jsx)("hr",{}),(0,u.jsx)("div",{className:"margin-vert--md",children:(0,u.jsx)(at,{error:e})})]})})})}const kt="mainWrapper_z2l0";function St(e){const{children:t,noFooter:n,wrapperClassName:r,title:l,description:s}=e;return(0,b.J)(),(0,u.jsxs)(yt,{children:[(0,u.jsx)(i.be,{title:l,description:s}),(0,u.jsx)(y,{}),(0,u.jsx)(N,{}),(0,u.jsx)(ht,{}),(0,u.jsx)("div",{id:d,className:(0,o.A)(g.G.layout.main.container,g.G.wrapper.main,kt,r),children:(0,u.jsx)(a.A,{fallback:e=>(0,u.jsx)(wt,{...e}),children:t})}),!n&&(0,u.jsx)(bt,{})]})}},8774(e,t,n){"use strict";n.d(t,{A:()=>p});var r=n(6540),o=n(4625),a=n(440),i=n(4586),l=n(6654),s=n(8193),c=n(3427),u=n(6025),d=n(4848);function f({isNavLink:e,to:t,href:n,activeClassName:f,isActive:p,"data-noBrokenLinkCheck":h,autoAddBaseUrl:m=!0,...g},b){const{siteConfig:v}=(0,i.A)(),{trailingSlash:y,baseUrl:x}=v,w=v.future.experimental_router,{withBaseUrl:k}=(0,u.hH)(),S=(0,c.A)(),C=(0,r.useRef)(null);(0,r.useImperativeHandle)(b,()=>C.current);const E=t||n;const j=(0,l.A)(E),_=E?.replace("pathname://","");let O=void 0!==_?(T=_,m&&(e=>e.startsWith("/"))(T)?k(T):T):void 0;var T;"hash"===w&&O?.startsWith("./")&&(O=O?.slice(1)),O&&j&&(O=(0,a.Ks)(O,{trailingSlash:y,baseUrl:x}));const P=(0,r.useRef)(!1),A=e?o.k2:o.N_,N=s.A.canUseIntersectionObserver,M=(0,r.useRef)(),L=()=>{P.current||null==O||(window.docusaurus.preload(O),P.current=!0)};(0,r.useEffect)(()=>(!N&&j&&s.A.canUseDOM&&null!=O&&window.docusaurus.prefetch(O),()=>{N&&M.current&&M.current.disconnect()}),[M,O,N,j]);const I=O?.startsWith("#")??!1,R=!g.target||"_self"===g.target,$=!O||!j||!R||I&&"hash"!==w;h||!I&&$||S.collectLink(O),g.id&&S.collectAnchor(g.id);const F={};return $?(0,d.jsx)("a",{ref:C,href:O,...E&&!j&&{target:"_blank",rel:"noopener noreferrer"},...g,...F}):(0,d.jsx)(A,{...g,onMouseEnter:L,onTouchStart:L,innerRef:e=>{C.current=e,N&&e&&j&&(M.current=new window.IntersectionObserver(t=>{t.forEach(t=>{e===t.target&&(t.isIntersecting||t.intersectionRatio>0)&&(M.current.unobserve(e),M.current.disconnect(),null!=O&&window.docusaurus.prefetch(O))})}),M.current.observe(e))},to:O,...e&&{isActive:p,activeClassName:f},...F})}const p=r.forwardRef(f)},8853(e,t,n){"use strict";n.d(t,{fz:()=>l,iG:()=>a});var r=n(4848);function o({style:e,className:t}){return(0,r.jsx)("div",{style:e,className:t,children:(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"10",height:"15",viewBox:"0 0 10 15",fill:"none",children:(0,r.jsx)("path",{d:"M5 5V0H0V5V10V15H5H10V5H5Z",fill:"#ED168F"})})})}function a({style:e}){return(0,r.jsxs)("div",{style:{display:"flex",gap:5,...e},children:[(0,r.jsx)(o,{}),(0,r.jsx)(o,{})]})}function i({style:e,className:t}){return(0,r.jsx)("div",{style:e,className:t,children:(0,r.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"10",height:"15",viewBox:"0 0 10 15",fill:"none",children:(0,r.jsx)("path",{d:"M5 0H0V5V10H5V15H10V10V5V0H5Z",fill:"#ED168F"})})})}function l({style:e}){return(0,r.jsxs)("div",{style:{display:"flex",justifyContent:"flex-end",gap:5,...e},children:[(0,r.jsx)(i,{}),(0,r.jsx)(i,{})]})}},8857(e,t,n){"use strict";n.d(t,{Ms:()=>b,vU:()=>p});var r=n(4629),o=n(1693),a=n(2258),i=n(2236),l=n(6712);function s(){}var c=u("C",void 0,void 0);function u(e,t,n){return{kind:e,value:t,error:n}}var d=n(2332),f=n(3004),p=function(e){function t(t){var n=e.call(this)||this;return n.isStopped=!1,t?(n.destination=t,(0,a.Uv)(t)&&t.add(n)):n.destination=x,n}return(0,r.C6)(t,e),t.create=function(e,t,n){return new b(e,t,n)},t.prototype.next=function(e){this.isStopped?y(function(e){return u("N",e,void 0)}(e),this):this._next(e)},t.prototype.error=function(e){this.isStopped?y(u("E",void 0,e),this):(this.isStopped=!0,this._error(e))},t.prototype.complete=function(){this.isStopped?y(c,this):(this.isStopped=!0,this._complete())},t.prototype.unsubscribe=function(){this.closed||(this.isStopped=!0,e.prototype.unsubscribe.call(this),this.destination=null)},t.prototype._next=function(e){this.destination.next(e)},t.prototype._error=function(e){try{this.destination.error(e)}finally{this.unsubscribe()}},t.prototype._complete=function(){try{this.destination.complete()}finally{this.unsubscribe()}},t}(a.yU),h=Function.prototype.bind;function m(e,t){return h.call(e,t)}var g=function(){function e(e){this.partialObserver=e}return e.prototype.next=function(e){var t=this.partialObserver;if(t.next)try{t.next(e)}catch(n){v(n)}},e.prototype.error=function(e){var t=this.partialObserver;if(t.error)try{t.error(e)}catch(n){v(n)}else v(e)},e.prototype.complete=function(){var e=this.partialObserver;if(e.complete)try{e.complete()}catch(t){v(t)}},e}(),b=function(e){function t(t,n,r){var a,l,s=e.call(this)||this;(0,o.T)(t)||!t?a={next:null!=t?t:void 0,error:null!=n?n:void 0,complete:null!=r?r:void 0}:s&&i.$.useDeprecatedNextContext?((l=Object.create(t)).unsubscribe=function(){return s.unsubscribe()},a={next:t.next&&m(t.next,l),error:t.error&&m(t.error,l),complete:t.complete&&m(t.complete,l)}):a=t;return s.destination=new g(a),s}return(0,r.C6)(t,e),t}(p);function v(e){i.$.useDeprecatedSynchronousErrorHandling?(0,f.l)(e):(0,l.m)(e)}function y(e,t){var n=i.$.onStoppedNotification;n&&d.f.setTimeout(function(){return n(e,t)})}var x={closed:!0,next:s,error:function(e){throw e},complete:s}},8896(e,t,n){"use strict";n.d(t,{s:()=>r});var r="function"==typeof Symbol&&Symbol.observable||"@@observable"},9092(e,t,n){"use strict";n.d(t,{B:()=>c});var r=n(4629),o=n(1753),a=n(2258),i=(0,n(5383).L)(function(e){return function(){e(this),this.name="ObjectUnsubscribedError",this.message="object unsubscribed"}}),l=n(34),s=n(3004),c=function(e){function t(){var t=e.call(this)||this;return t.closed=!1,t.currentObservers=null,t.observers=[],t.isStopped=!1,t.hasError=!1,t.thrownError=null,t}return(0,r.C6)(t,e),t.prototype.lift=function(e){var t=new u(this,this);return t.operator=e,t},t.prototype._throwIfClosed=function(){if(this.closed)throw new i},t.prototype.next=function(e){var t=this;(0,s.Y)(function(){var n,o;if(t._throwIfClosed(),!t.isStopped){t.currentObservers||(t.currentObservers=Array.from(t.observers));try{for(var a=(0,r.Ju)(t.currentObservers),i=a.next();!i.done;i=a.next()){i.value.next(e)}}catch(l){n={error:l}}finally{try{i&&!i.done&&(o=a.return)&&o.call(a)}finally{if(n)throw n.error}}}})},t.prototype.error=function(e){var t=this;(0,s.Y)(function(){if(t._throwIfClosed(),!t.isStopped){t.hasError=t.isStopped=!0,t.thrownError=e;for(var n=t.observers;n.length;)n.shift().error(e)}})},t.prototype.complete=function(){var e=this;(0,s.Y)(function(){if(e._throwIfClosed(),!e.isStopped){e.isStopped=!0;for(var t=e.observers;t.length;)t.shift().complete()}})},t.prototype.unsubscribe=function(){this.isStopped=this.closed=!0,this.observers=this.currentObservers=null},Object.defineProperty(t.prototype,"observed",{get:function(){var e;return(null===(e=this.observers)||void 0===e?void 0:e.length)>0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(t){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,t)},t.prototype._subscribe=function(e){return this._throwIfClosed(),this._checkFinalizedStatuses(e),this._innerSubscribe(e)},t.prototype._innerSubscribe=function(e){var t=this,n=this,r=n.hasError,o=n.isStopped,i=n.observers;return r||o?a.Kn:(this.currentObservers=null,i.push(e),new a.yU(function(){t.currentObservers=null,(0,l.o)(i,e)}))},t.prototype._checkFinalizedStatuses=function(e){var t=this,n=t.hasError,r=t.thrownError,o=t.isStopped;n?e.error(r):o&&e.complete()},t.prototype.asObservable=function(){var e=new o.c;return e.source=this,e},t.create=function(e,t){return new u(e,t)},t}(o.c),u=function(e){function t(t,n){var r=e.call(this)||this;return r.destination=t,r.source=n,r}return(0,r.C6)(t,e),t.prototype.next=function(e){var t,n;null===(n=null===(t=this.destination)||void 0===t?void 0:t.next)||void 0===n||n.call(t,e)},t.prototype.error=function(e){var t,n;null===(n=null===(t=this.destination)||void 0===t?void 0:t.error)||void 0===n||n.call(t,e)},t.prototype.complete=function(){var e,t;null===(t=null===(e=this.destination)||void 0===e?void 0:e.complete)||void 0===t||t.call(e)},t.prototype._subscribe=function(e){var t,n;return null!==(n=null===(t=this.source)||void 0===t?void 0:t.subscribe(e))&&void 0!==n?n:a.Kn},t}(c)},9169(e,t,n){"use strict";n.d(t,{Dt:()=>l,ys:()=>i});var r=n(6540),o=n(8328),a=n(4586);function i(e,t){const n=e=>(!e||e.endsWith("/")?e:`${e}/`)?.toLowerCase();return n(e)===n(t)}function l(){const{baseUrl:e}=(0,a.A)().siteConfig;return(0,r.useMemo)(()=>function({baseUrl:e,routes:t}){function n(t){return t.path===e&&!0===t.exact}function r(t){return t.path===e&&!t.exact}return function e(t){if(0===t.length)return;return t.find(n)||e(t.filter(r).flatMap(e=>e.routes??[]))}(t)}({routes:o.A,baseUrl:e}),[e])}},9514(e,t,n){"use strict";n.d(t,{A:()=>l});var r,o,a=n(6540);function i(){return i=Object.assign?Object.assign.bind():function(e){for(var t=1;ta.createElement("svg",i({xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 21","aria-labelledby":t},n),e?a.createElement("title",{id:t},e):null,r||(r=a.createElement("g",{clipPath:"url(#a)"},a.createElement("path",{fill:"#fff",d:"M17.3 17.6h-3V13c0-1 0-2.5-1.6-2.5-1.5 0-1.8 1.2-1.8 2.5v4.7H8V8h2.9v1.3a3 3 0 0 1 2.9-1.6c3 0 3.6 2 3.6 4.6zM4.5 6.6a1.7 1.7 0 1 1 0-3.5 1.7 1.7 0 0 1 0 3.5m1.5 11H3V8h3zM18.8.4H1.5A1.5 1.5 0 0 0 0 1.8v17.4a1.5 1.5 0 0 0 1.5 1.4h17.3a1.5 1.5 0 0 0 1.5-1.4V1.8A1.5 1.5 0 0 0 18.8.4"}))),o||(o=a.createElement("defs",null,a.createElement("clipPath",{id:"a"},a.createElement("path",{fill:"#fff",d:"M0 .4h24v20.5H0z"})))))},9529(e,t,n){"use strict";n.d(t,{A:()=>g});var r=n(6540),o=n(8774),a=n(6025),i=n(4586),l=n(6342),s=n(4164),c=n(2303),u=n(5293);const d={themedComponent:"themedComponent_mlkZ","themedComponent--light":"themedComponent--light_NVdE","themedComponent--dark":"themedComponent--dark_xIcU"};var f=n(4848);function p({className:e,children:t}){const n=(0,c.A)(),{colorMode:o}=(0,u.G)();return(0,f.jsx)(f.Fragment,{children:(n?"dark"===o?["dark"]:["light"]:["light","dark"]).map(n=>{const o=t({theme:n,className:(0,s.A)(e,d.themedComponent,d[`themedComponent--${n}`])});return(0,f.jsx)(r.Fragment,{children:o},n)})})}function h(e){const{sources:t,className:n,alt:r,...o}=e;return(0,f.jsx)(p,{className:n,children:({theme:e,className:n})=>(0,f.jsx)("img",{src:t[e],alt:r,className:n,...o})})}function m({logo:e,alt:t,imageClassName:n}){const r={light:(0,a.Ay)(e.src),dark:(0,a.Ay)(e.srcDark||e.src)},o=(0,f.jsx)(h,{className:e.className,sources:r,height:e.height,width:e.width,alt:t,style:e.style});return n?(0,f.jsx)("div",{className:n,children:o}):o}function g(e){const{siteConfig:{title:t}}=(0,i.A)(),{navbar:{title:n,logo:r}}=(0,l.p)(),{imageClassName:s,titleClassName:c,...u}=e,d=(0,a.Ay)(r?.href||"/"),p=n?"":t,h=r?.alt??p;return(0,f.jsxs)(o.A,{to:d,...u,...r?.target&&{target:r.target},children:[r&&(0,f.jsx)(m,{logo:r,alt:h,imageClassName:s}),null!=n&&(0,f.jsx)("b",{className:c,children:n})]})}},9532(e,t,n){"use strict";n.d(t,{Be:()=>c,ZC:()=>l,_q:()=>i,dV:()=>s,fM:()=>u});var r=n(6540),o=n(205),a=n(4848);function i(e){const t=(0,r.useRef)(e);return(0,o.A)(()=>{t.current=e},[e]),(0,r.useCallback)((...e)=>t.current(...e),[])}function l(e){const t=(0,r.useRef)();return(0,o.A)(()=>{t.current=e}),t.current}class s extends Error{constructor(e,t){super(),this.name="ReactContextError",this.message=`Hook ${this.stack?.split("\n")[1]?.match(/at (?:\w+\.)?(?\w+)/)?.groups.name??""} is called outside the <${e}>. ${t??""}`}}function c(e){const t=Object.entries(e);return t.sort((e,t)=>e[0].localeCompare(t[0])),(0,r.useMemo)(()=>e,t.flat())}function u(e){return({children:t})=>(0,a.jsx)(a.Fragment,{children:e.reduceRight((e,t)=>(0,a.jsx)(t,{children:e}),t)})}},9698(e,t){"use strict";var n=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function o(e,t,r){var o=null;if(void 0!==r&&(o=""+r),void 0!==t.key&&(o=""+t.key),"key"in t)for(var a in r={},t)"key"!==a&&(r[a]=t[a]);else r=t;return t=r.ref,{$$typeof:n,type:e,key:o,ref:void 0!==t?t:null,props:r}}t.Fragment=r,t.jsx=o,t.jsxs=o},9869(e,t){"use strict";var n=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),l=Symbol.for("react.consumer"),s=Symbol.for("react.context"),c=Symbol.for("react.forward_ref"),u=Symbol.for("react.suspense"),d=Symbol.for("react.memo"),f=Symbol.for("react.lazy"),p=Symbol.for("react.activity"),h=Symbol.iterator;var m={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},g=Object.assign,b={};function v(e,t,n){this.props=e,this.context=t,this.refs=b,this.updater=n||m}function y(){}function x(e,t,n){this.props=e,this.context=t,this.refs=b,this.updater=n||m}v.prototype.isReactComponent={},v.prototype.setState=function(e,t){if("object"!=typeof e&&"function"!=typeof e&&null!=e)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")},v.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")},y.prototype=v.prototype;var w=x.prototype=new y;w.constructor=x,g(w,v.prototype),w.isPureReactComponent=!0;var k=Array.isArray;function S(){}var C={H:null,A:null,T:null,S:null},E=Object.prototype.hasOwnProperty;function j(e,t,r){var o=r.ref;return{$$typeof:n,type:e,key:t,ref:void 0!==o?o:null,props:r}}function _(e){return"object"==typeof e&&null!==e&&e.$$typeof===n}var O=/\/+/g;function T(e,t){return"object"==typeof e&&null!==e&&null!=e.key?(n=""+e.key,r={"=":"=0",":":"=2"},"$"+n.replace(/[=:]/g,function(e){return r[e]})):t.toString(36);var n,r}function P(e,t,o,a,i){var l=typeof e;"undefined"!==l&&"boolean"!==l||(e=null);var s,c,u=!1;if(null===e)u=!0;else switch(l){case"bigint":case"string":case"number":u=!0;break;case"object":switch(e.$$typeof){case n:case r:u=!0;break;case f:return P((u=e._init)(e._payload),t,o,a,i)}}if(u)return i=i(e),u=""===a?"."+T(e,0):a,k(i)?(o="",null!=u&&(o=u.replace(O,"$&/")+"/"),P(i,t,o,"",function(e){return e})):null!=i&&(_(i)&&(s=i,c=o+(null==i.key||e&&e.key===i.key?"":(""+i.key).replace(O,"$&/")+"/")+u,i=j(s.type,c,s.props)),t.push(i)),1;u=0;var d,p=""===a?".":a+":";if(k(e))for(var m=0;mh});var r=n(6540),o=n(544),a=n(4848);function i({iconUrl:e,iconVb:t=20,dark:n,...r}){const o=t,i=t;return(0,a.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",width:r.width??44,height:r.height??76,viewBox:"0 0 44 76",fill:"none",...r,children:[(0,a.jsx)("defs",{children:(0,a.jsx)("clipPath",{id:"phoneScreenClip",children:(0,a.jsx)("path",{d:"M35.08 2.22003H8.48003C4.77972 2.22003 1.78003 5.21972 1.78003 8.92003V67.52C1.78003 71.2203 4.77972 74.22 8.48003 74.22H35.08C38.7803 74.22 41.78 71.2203 41.78 67.52V8.92003C41.78 5.21972 38.7803 2.22003 35.08 2.22003Z"})})}),(0,a.jsxs)("g",{clipPath:"url(#clip0)",children:[(0,a.jsx)("path",{d:"M35.08 2.22003H8.48003C4.77972 2.22003 1.78003 5.21972 1.78003 8.92003V67.52C1.78003 71.2203 4.77972 74.22 8.48003 74.22H35.08C38.7803 74.22 41.78 71.2203 41.78 67.52V8.92003C41.78 5.21972 38.7803 2.22003 35.08 2.22003Z",fill:n?"#20293C":"#0D0F18",stroke:"white",strokeWidth:"3.56",strokeMiterlimit:"10",strokeLinecap:"round"}),(0,a.jsx)("path",{d:"M16.78 9.72003H26.78",stroke:"white",strokeWidth:"4",strokeMiterlimit:"10",strokeLinecap:"round"})]}),e&&(0,a.jsx)("g",{clipPath:"url(#phoneScreenClip)",children:(0,a.jsx)("g",{transform:"translate(21.78 41.22)",children:(0,a.jsx)("image",{href:e,x:-o/2,y:-i/2,width:o,height:i,preserveAspectRatio:"xMidYMid meet"})})}),(0,a.jsx)("defs",{children:(0,a.jsx)("clipPath",{id:"clip0",children:(0,a.jsx)("rect",{width:"43.56",height:"75.56",fill:"white",transform:"translate(0 0.440002)"})})})]})}function l({iconUrl:e,iconVb:t=16,dark:n,...r}){const o=t,i=t;return(0,a.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",width:r.width??56,height:r.height??80,viewBox:"0 0 56 80",fill:"none",...r,children:[(0,a.jsx)("defs",{children:(0,a.jsx)("clipPath",{id:"smartwatchScreenClip",children:(0,a.jsx)("path",{d:"M37.33 18.37H10.67C6.98626 18.37 4 21.3563 4 25.04V54.96C4 58.6437 6.98626 61.63 10.67 61.63H37.33C41.0137 61.63 44 58.6437 44 54.96V25.04C44 21.3563 41.0137 18.37 37.33 18.37Z"})})}),(0,a.jsx)("path",{d:"M10.23 18.42L11.89 9.8C12.57 6.37 15.61 3.93 19.1 4H28.76C32.25 3.93 35.29 6.38 35.97 9.8L37.69 18.42",stroke:"white",strokeWidth:"4",strokeLinecap:"round",strokeLinejoin:"round"}),(0,a.jsx)("path",{d:"M37.9601 61.69L36.2601 70.2C35.5801 73.63 32.5401 76.07 29.0501 76H19.2401C15.7501 76.07 12.7101 73.62 12.0301 70.2L10.3301 61.69",stroke:"white",strokeWidth:"4",strokeLinecap:"round",strokeLinejoin:"round"}),(0,a.jsx)("path",{d:"M37.33 18.37H10.67C6.98626 18.37 4 21.3563 4 25.04V54.96C4 58.6437 6.98626 61.63 10.67 61.63H37.33C41.0137 61.63 44 58.6437 44 54.96V25.04C44 21.3563 41.0137 18.37 37.33 18.37Z",fill:n?"#20293C":"#0D0F18",stroke:"white",strokeWidth:"4",strokeLinecap:"round",strokeLinejoin:"round"}),(0,a.jsx)("path",{d:"M48 36.44V29.24",stroke:"white",strokeWidth:"4",strokeLinecap:"round",strokeLinejoin:"round"}),e&&(0,a.jsx)("g",{clipPath:"url(#smartwatchScreenClip)",children:(0,a.jsx)("g",{transform:"translate(24 40)",children:(0,a.jsx)("image",{href:e,x:-o/2,y:-i/2,width:o,height:i,preserveAspectRatio:"xMidYMid meet"})})})]})}var s=n(199),c=n(8193);const u=({darkMode:e=!1,style:t,className:n,hasIcon:o=!0})=>{const[,i]=(0,r.useState)(0),[l,u]=(0,r.useState)(s.S[0].iconUrl);(0,r.useEffect)(()=>{if(!c.A.canUseDOM)return;const e=()=>{i(e=>{const t=(e+1)%s.S.length,n=s.S[t].iconUrl;return u(n),t})};return window.addEventListener("heartbeat",e),()=>window.removeEventListener("heartbeat",e)},[]);const d={position:"relative",display:"inline-flex",alignItems:"center",justifyContent:"center",borderRadius:12,WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d",...t},f={width:"56px",height:"56px",background:e?"var(--bg-color)":"var(--bg-color-dark)",borderRadius:"50%",display:"flex",alignItems:"center",justifyContent:"center",border:`4px solid ${e?"var(--bg-color-dark)":"var(--bg-color)"}`,position:"relative",overflow:"hidden",WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d"};return(0,a.jsxs)("div",{style:d,className:n,children:[(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",width:"126",height:"96",viewBox:"0 0 126 96",fill:"none",children:(0,a.jsx)("path",{d:"M99.6095 31.2864H92.5913C91.846 31.2864 91.1939 30.799 90.961 30.0949C86.2952 16.3386 73.3224 7.00012 58.6573 7.00012C39.8154 7.00012 24.537 22.211 24.537 40.9885V41.004C24.537 41.9634 23.7684 42.7525 22.8057 42.7525H20.5621C11.7894 42.7525 4.32097 49.5533 4.01043 58.2883C3.68437 67.4566 11.0441 75.0001 20.1739 75.0001H100.067C112.388 75.0001 122.333 64.8802 121.991 52.532C121.65 40.1839 111.565 31.2864 99.6095 31.2864Z",fill:"white",stroke:"white",strokeWidth:4})}),o&&(0,a.jsx)("div",{style:{position:"absolute",left:"50%",top:"50%",transform:"translate(-50%, -10%)",WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d"},children:(0,a.jsx)("div",{style:f,children:l&&(0,a.jsx)("img",{draggable:!1,src:l,alt:"icon",style:{position:"absolute",inset:0,margin:"auto",maxWidth:"60%",maxHeight:"60%",WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d"}})})})]})};function d({iconUrl:e,iconVb:t=21,dark:n,...r}){const o=t,i=t;return(0,a.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",width:r.width??80,height:r.height??73,viewBox:"0 0 80 73",fill:"none",...r,children:[(0,a.jsx)("defs",{children:(0,a.jsx)("clipPath",{id:"desktopScreenClip",children:(0,a.jsx)("path",{d:"M69.33 4.20001H10.67C6.98626 4.20001 4 7.18627 4 10.87V47.93C4 51.6138 6.98626 54.6 10.67 54.6H69.33C73.0137 54.6 76 51.6138 76 47.93V10.87C76 7.18627 73.0137 4.20001 69.33 4.20001Z"})})}),(0,a.jsx)("path",{d:"M69.33 4.20001H10.67C6.98626 4.20001 4 7.18627 4 10.87V47.93C4 51.6138 6.98626 54.6 10.67 54.6H69.33C73.0137 54.6 76 51.6138 76 47.93V10.87C76 7.18627 73.0137 4.20001 69.33 4.20001Z",fill:n?"#20293C":"#0D0F18",stroke:"white",strokeWidth:"4",strokeLinecap:"round",strokeLinejoin:"round"}),(0,a.jsx)("path",{d:"M25.6001 69H54.4001",stroke:"white",strokeWidth:"4",strokeLinecap:"round",strokeLinejoin:"round"}),(0,a.jsx)("path",{d:"M40 54.6V69",stroke:"white",strokeWidth:"4",strokeLinecap:"round",strokeLinejoin:"round"}),e&&(0,a.jsx)("g",{clipPath:"url(#desktopScreenClip)",children:(0,a.jsx)("g",{transform:`translate(40 ${(4.2+54.6)/2})`,children:(0,a.jsx)("image",{href:e,x:-o/2,y:-i/2,width:o,height:i,preserveAspectRatio:"xMidYMid meet"})})})]})}function f({iconUrl:e,iconVb:t=24,dark:n,...r}){const o=t,i=t;return(0,a.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",width:r.width??55,height:r.height??76,viewBox:"0 0 55 76",fill:"none",...r,children:[(0,a.jsx)("defs",{children:(0,a.jsx)("clipPath",{id:"tabletScreenClip",children:(0,a.jsx)("path",{d:"M45.7 2H8.7C4.99969 2 2 4.99969 2 8.7V67.3C2 71.0003 4.99969 74 8.7 74H45.7C49.4003 74 52.4 71.0003 52.4 67.3V8.7C52.4 4.99969 49.4003 2 45.7 2Z"})})}),(0,a.jsxs)("g",{clipPath:"url(#clip0_752_214)",children:[(0,a.jsx)("path",{d:"M45.7 2H8.7C4.99969 2 2 4.99969 2 8.7V67.3C2 71.0003 4.99969 74 8.7 74H45.7C49.4003 74 52.4 71.0003 52.4 67.3V8.7C52.4 4.99969 49.4003 2 45.7 2Z",fill:n?"#20293C":"#0D0F18",stroke:"white",strokeWidth:"4",strokeMiterlimit:"10",strokeLinecap:"round"}),(0,a.jsx)("path",{d:"M27.2 66.34C26.15 66.34 25.12 65.91 24.37 65.17C24.19 64.98 24.0199 64.78 23.8799 64.56C23.7299 64.34 23.61 64.11 23.51 63.87C23.41 63.63 23.33 63.37 23.28 63.12C23.23 62.86 23.2 62.6 23.2 62.34C23.2 62.08 23.23 61.81 23.28 61.56C23.33 61.3 23.41 61.05 23.51 60.81C23.61 60.57 23.7299 60.34 23.8799 60.12C24.0199 59.9 24.19 59.69 24.37 59.51C25.3 58.58 26.6799 58.15 27.9799 58.42C28.24 58.47 28.49 58.54 28.7299 58.64C28.9699 58.74 29.2 58.87 29.42 59.01C29.64 59.16 29.85 59.33 30.03 59.51C30.21 59.69 30.38 59.9 30.53 60.12C30.67 60.34 30.7899 60.57 30.8899 60.81C30.9899 61.05 31.07 61.3 31.12 61.56C31.17 61.81 31.2 62.08 31.2 62.34C31.2 62.6 31.17 62.86 31.12 63.12C31.07 63.37 30.9899 63.63 30.8899 63.87C30.7899 64.11 30.67 64.34 30.53 64.56C30.38 64.78 30.21 64.98 30.03 65.17C29.28 65.91 28.25 66.34 27.2 66.34Z",fill:"white"})]}),e&&(0,a.jsx)("g",{clipPath:"url(#tabletScreenClip)",children:(0,a.jsx)("g",{transform:"translate(27.2 32)",children:(0,a.jsx)("image",{href:e,x:-o/2,y:-i/2,width:o,height:i,preserveAspectRatio:"xMidYMid meet"})})}),(0,a.jsx)("defs",{children:(0,a.jsx)("clipPath",{id:"clip0_752_214",children:(0,a.jsx)("rect",{width:"54.4",height:"76",fill:"white"})})})]})}const p=({className:e,style:t,...n})=>(0,a.jsxs)("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",stroke:"#fff",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,viewBox:"0 0 24 24",className:e,style:t,...n,children:[(0,a.jsx)("path",{d:"M12 20z"}),(0,a.jsx)("path",{d:"M8.5 16.43a5 5 0 0 1 7 0"}),(0,a.jsx)("path",{d:"M5 12.86a10 10 0 0 1 5.17-2.7"}),(0,a.jsx)("path",{d:"M19 12.86a10 10 0 0 0-2-1.52"}),(0,a.jsx)("path",{d:"M2 8.82a15 15 0 0 1 4.18-2.64"}),(0,a.jsx)("path",{d:"M22 8.82a15 15 0 0 0-11.29-3.76"}),(0,a.jsx)("path",{d:"M2 2l20 20"})]});function h({scale:e=1,dark:t,hasIcon:n=!0,demoOffline:s=!1}){const h=(0,r.useRef)(null),[,g]=(0,r.useState)(0);(0,r.useEffect)(()=>{const e=()=>g(e=>e+1);return window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[]);const b=["desktop","tablet","phone","desktop","smartwatch"],[v,y]=(0,r.useState)(0),x=(0,r.useRef)(0),[w,k]=(0,r.useState)(null),[S,C]=(0,r.useState)(null),E=(0,r.useRef)(null);(0,r.useEffect)(()=>{E.current=S},[S]);const j=["var(--color-top)","var(--color-middle)","var(--color-bottom)"],[_,O]=(0,r.useState)(j[0]);(0,r.useEffect)(()=>{if(!c.A.canUseDOM)return;const e=e=>{if(b.length<=1)return 0;let t=Math.floor(Math.random()*b.length),n=0;for(;null!==e&&t===e&&n<50;)t=Math.floor(Math.random()*b.length),n++;return t},t=()=>{x.current+=1;const t=x.current;y(t);let n=E.current;s&&t%2==0&&(n=b.length<=1?0:e(E.current),E.current=n,C(n));const r=s?n??E.current:null,o=e(r);k(o),O(j[Math.floor(Math.random()*j.length)])};return window.addEventListener("heartbeat",t),()=>window.removeEventListener("heartbeat",t)},[s,b.length]);const T=250*e,P=200*e,A=60*e,N=b.length,M=45*e,L=P-M,I=-Math.PI/2,R=Array.from({length:N},(e,t)=>{const n=I+2*Math.PI*t/N,r=T+L*Math.cos(n),o=P+L*Math.sin(n),a=r-T,i=o-P,l=Math.sqrt(a*a+i*i)-(A+3)-(M+7);return{angleDeg:180*Math.atan2(i,a)/Math.PI,lineLength:l,deviceX:r,deviceY:o,inwardName:`deviceToCenter-${t}-${v}`,outwardName:`centerToDevice-${t}-${v}`,deviceLeft:r-M,deviceRight:r+M,deviceTop:o-M,deviceBottom:o+M}}),$=T-A,F=T+A,z=P-A,D=P+A,B=Math.min($,...R.map(e=>e.deviceLeft)),H=Math.max(F,...R.map(e=>e.deviceRight)),V=Math.min(z,...R.map(e=>e.deviceTop)),U=Math.max(D,...R.map(e=>e.deviceBottom)),q=Math.ceil(H-B),W=Math.ceil(U-V),G=-B,X=-V,K=Math.max(200,Math.floor(.45*o.HEARTBEAT_DURATION)),Y=Math.max(200,Math.floor(.45*o.HEARTBEAT_DURATION)),Q=Math.max(0,o.HEARTBEAT_DURATION-(K+Y)),Z=R.map(({inwardName:e,outwardName:t,lineLength:n})=>`\n@keyframes ${e} {\n 0% { transform: translateX(${n}px); opacity: 0; }\n 10% { opacity: 1; }\n 90% { opacity: 1; }\n 100% { transform: translateX(0px); opacity: 0; }\n}\n@keyframes ${t} {\n 0% { transform: translateX(0px); opacity: 0; }\n 10% { opacity: 1; }\n 90% { opacity: 1; }\n 100% { transform: translateX(${n}px); opacity: 0; }\n}`).join("\n");return(0,a.jsx)("div",{ref:h,style:{...m.container,"--packetColor":_,display:"flex",justifyContent:"center",WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d"},children:(0,a.jsxs)("div",{style:{position:"relative",width:`${q}px`,height:`${W}px`,WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d"},children:[(0,a.jsx)("div",{className:"device",style:{position:"absolute",left:T-A+G,top:P-A+X,width:2*A,height:2*A,justifyContent:"center",WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d"},children:(0,a.jsx)(u,{darkMode:t,style:{width:"100%"},hasIcon:n})}),R.map(({angleDeg:e,lineLength:n,deviceX:o,deviceY:c,inwardName:u,outwardName:h},m)=>{const g=A+3,y=b[m],x=w===m,k=s&&S===m;return(0,a.jsxs)(r.Fragment,{children:[(0,a.jsxs)("div",{style:{position:"absolute",borderRadius:5,left:T+G,top:P+X,width:n,height:"2px",backgroundColor:k?"rgba(255,255,255,0.25)":"white",transform:`rotate(${e}deg) translateX(${g}px)`,transformOrigin:"left center",justifyContent:"center",textAlign:"center",display:"flex"},children:[k&&(0,a.jsx)("div",{style:{position:"absolute",left:"50%",top:"50%",transform:`translate(-50%, -50%) rotate(${-e}deg)`,transformOrigin:"center",background:t?"var(--bg-color-dark)":"var(--bg-color-darkest)",borderRadius:"50%",padding:4,display:"flex",alignItems:"center",justifyContent:"center"},children:(0,a.jsx)(p,{style:{width:24,zIndex:2}})}),v>0&&x&&!k&&(0,a.jsx)("div",{style:{position:"absolute",left:0,top:"-6px",width:"12px",height:"12px",borderRadius:"50%",backgroundColor:_,boxShadow:`0 0 10px ${_}55`,animation:`${u} ${K}ms linear 1 forwards`,opacity:0}}),v>0&&!x&&!k&&(0,a.jsx)("div",{style:{position:"absolute",left:0,top:"-6px",width:"12px",height:"12px",borderRadius:"50%",backgroundColor:_,boxShadow:`0 0 10px ${_}55`,animation:`${h} ${Y}ms ${K+Q}ms linear 1 forwards`,opacity:0}})]}),(0,a.jsx)("div",{style:{position:"absolute",left:o-M+G,top:c-M+X,width:2*M,height:2*M,borderRadius:"50%",display:"flex",justifyContent:"center",textAlign:"center",alignItems:"center",verticalAlign:"middle",WebkitBackfaceVisibility:"hidden",backfaceVisibility:"hidden",transformStyle:"preserve-3d"},children:"phone"===y?(0,a.jsx)("div",{className:"device",style:{top:"20%",left:"30%"},children:(0,a.jsx)(i,{dark:t,iconUrl:"/files/logo/logo.svg"})}):"smartwatch"===y?(0,a.jsx)("div",{className:"device",style:{width:"80%",top:"20%",left:"17%"},children:(0,a.jsx)(l,{dark:t,iconUrl:"/files/logo/logo.svg"})}):"desktop"===y?(0,a.jsx)("div",{className:"device",style:{top:"20%",left:"27%"},children:(0,a.jsx)(d,{dark:t,iconUrl:"/files/logo/logo.svg"})}):(0,a.jsx)("div",{className:"device",style:{top:"20%",left:"27%"},children:(0,a.jsx)(f,{dark:t,iconUrl:"/files/logo/logo.svg"})})})]},m)}),(0,a.jsx)("style",{children:Z})]})})}const m={container:{width:"100%",overflow:"visible"}}},9982(e,t,n){"use strict";e.exports=n(4477)}},e=>{e.O(0,[1869],()=>{return t=6019,e(e.s=t);var t});e.O()}]);
\ No newline at end of file
diff --git a/docs/assets/js/main.c9c8ff04.js.LICENSE.txt b/docs/assets/js/main.c9c8ff04.js.LICENSE.txt
deleted file mode 100644
index 330d3bcd542..00000000000
--- a/docs/assets/js/main.c9c8ff04.js.LICENSE.txt
+++ /dev/null
@@ -1,98 +0,0 @@
-/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
- * @license MIT */
-
-/*!
- Copyright (c) 2018 Jed Watson.
- Licensed under the MIT License (MIT), see
- http://jedwatson.github.io/classnames
-*/
-
-/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */
-
-/*!***************************************************
-* mark.js v8.11.1
-* https://markjs.io/
-* Copyright (c) 2014–2018, Julian Kühnel
-* Released under the MIT license https://git.io/vwTVl
-*****************************************************/
-
-/**
- * @license React
- * react-dom-client.production.js
- *
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * react-dom.production.js
- *
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * react-is.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * react-jsx-runtime.production.js
- *
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * react.production.js
- *
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/**
- * @license React
- * scheduler.production.js
- *
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/** @license React v16.13.1
- * react-is.production.min.js
- *
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-/** */
-
-/** */
-
-/** */
-
-/** */
-
-/** */
-
-/** */
diff --git a/docs/assets/js/runtime~main.f8cb08bf.js b/docs/assets/js/runtime~main.f8cb08bf.js
deleted file mode 100644
index 6aa21bf2e4b..00000000000
--- a/docs/assets/js/runtime~main.f8cb08bf.js
+++ /dev/null
@@ -1 +0,0 @@
-(()=>{"use strict";var e,a,d,f,b,c={},r={};function t(e){var a=r[e];if(void 0!==a)return a.exports;var d=r[e]={exports:{}};return c[e].call(d.exports,d,d.exports,t),d.exports}t.m=c,e=[],t.O=(a,d,f,b)=>{if(!d){var c=1/0;for(i=0;i=b)&&Object.keys(t.O).every(e=>t.O[e](d[o]))?d.splice(o--,1):(r=!1,b0&&e[i-1][2]>b;i--)e[i]=e[i-1];e[i]=[d,f,b]},t.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return t.d(a,{a:a}),a},d=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,t.t=function(e,f){if(1&f&&(e=this(e)),8&f)return e;if("object"==typeof e&&e){if(4&f&&e.__esModule)return e;if(16&f&&"function"==typeof e.then)return e}var b=Object.create(null);t.r(b);var c={};a=a||[null,d({}),d([]),d(d)];for(var r=2&f&&e;("object"==typeof r||"function"==typeof r)&&!~a.indexOf(r);r=d(r))Object.getOwnPropertyNames(r).forEach(a=>c[a]=()=>e[a]);return c.default=()=>e,t.d(b,c),b},t.d=(e,a)=>{for(var d in a)t.o(a,d)&&!t.o(e,d)&&Object.defineProperty(e,d,{enumerable:!0,get:a[d]})},t.f={},t.e=e=>Promise.all(Object.keys(t.f).reduce((a,d)=>(t.f[d](e,a),a),[])),t.u=e=>"assets/js/"+({10:"5134b15f",176:"280a2389",205:"3ebfb37f",268:"25626d15",405:"38a45a95",465:"4616b86a",561:"f44bb875",588:"84ae55a4",813:"b672caf7",833:"c0f75fb9",970:"15f1e21f",1018:"fe2a63b2",1054:"8a442806",1098:"a7f10198",1107:"b0889a22",1199:"4ed9495b",1235:"a7456010",1264:"6fa8aa1a",1400:"d4da9db3",1475:"0f6e10f0",1500:"a406dc27",1558:"f43e80a8",1567:"22dd74f7",1715:"9dd8ea89",1850:"8bc07e20",2055:"26b8a621",2061:"380cc66a",2076:"36715375",2078:"38bbf12a",2085:"fe7a07ee",2360:"e6b4453d",2373:"4f17bbdd",2584:"0e268d20",2586:"d2758528",2631:"8bfd920a",2633:"90102fdf",2777:"c4de80f8",2786:"86b4e356",2835:"0b761dc7",2845:"d622bd51",2853:"d04a108d",2966:"2456d5e0",3015:"85caacef",3021:"9e91b6f0",3129:"1b0f8c91",3148:"39600c95",3185:"714575d7",3321:"ae2c2832",3325:"9dae6e71",3469:"ed2d6610",3483:"13dc6548",3495:"7bbb96fd",3588:"b2653a00",3595:"931f4566",3633:"5b5afcec",3779:"b30f4f1f",3822:"8070e160",3852:"cbbe8f0a",3881:"5a273530",3916:"08ff000c",3949:"77d975e6",3988:"eb2e4b0c",3997:"68a466be",4013:"1b238727",4027:"8b4bf532",4028:"ebace26e",4132:"2efd0200",4134:"393be207",4141:"c6fdd490",4142:"2c41656d",4166:"92698a99",4194:"de9a3c97",4202:"91b454ee",4424:"432b83f9",4475:"045bd6f5",4557:"8b0a0922",4618:"6fd28feb",4630:"f15938da",4812:"25a43fd4",4853:"326aca46",4886:"eadd9b3c",4889:"51038524",4920:"feac4174",4962:"dc42ba65",4970:"37055470",4989:"0e467ee2",5101:"2fe9ecb2",5122:"924d6dd6",5123:"d20e74b4",5219:"401008a8",5265:"e8a836f3",5272:"118cde4c",5320:"6ae3580c",5335:"7815dd0c",5350:"3417a9c7",5353:"58215328",5423:"01a106d5",5448:"f61fdf57",5504:"98405524",5694:"21fa2740",5740:"c6349bb6",5742:"aba21aa0",5832:"34f94d1b",5852:"bdd39edd",5861:"77979bef",5866:"6187b59a",5966:"41f941a1",6061:"1f391b9e",6115:"951d0efb",6287:"cde77f4f",6355:"b8c49ce4",6386:"4777fd9a",6422:"6bfb0089",6465:"03e37916",6491:"ab919a1f",6543:"dbde2ffe",6584:"c44853e1",6616:"01684a0a",6717:"55a5b596",6723:"8aa53ed7",6724:"2564bf4f",6797:"e24529eb",6861:"84a3af36",6866:"187b985e",6953:"1c0701dd",6998:"ee1b9f21",7098:"a7bd4aaa",7149:"a574e172",7249:"c9c8e0b6",7277:"61792630",7320:"db34d6b0",7396:"ec526260",7408:"6cbff7c2",7498:"2aec6989",7513:"f14ec96f",7537:"0596642b",7700:"8489a755",7722:"ff492cda",7793:"c7b47308",7817:"502d8946",7836:"820807a1",7853:"35e4d287",8191:"32667c41",8318:"badcd764",8382:"0027230a",8401:"17896441",8413:"1db64337",8545:"4af60d2e",8588:"1e0353aa",8674:"ad16b3ea",8715:"597d88be",8744:"294ac9d5",8760:"a442adcd",8845:"f490b64c",8897:"11d75f9a",8907:"f1c185f0",8926:"51334108",9048:"a94703ab",9111:"1b5fa8ad",9146:"c843a053",9167:"c3bc9c50",9192:"ac62b32d",9257:"8a22f3a9",9408:"a69eebfc",9460:"51014a8a",9515:"8084fe3b",9548:"4adf80bb",9591:"4ba7e5a3",9592:"7f02c700",9594:"40b6398a",9647:"5e95c892",9743:"1da545ff",9772:"14d72841",9796:"0e945c41",9824:"aa14e6b1",9881:"8288c265",9997:"8bc82b1f"}[e]||e)+"."+{10:"c48f7dbd",176:"3dec6d46",205:"f5f42f4c",268:"27c1b41f",405:"a7eb0903",465:"247bc33b",561:"0c73cd1a",588:"1bdc94ff",813:"019a15fb",833:"ea90dae3",970:"306cc5e2",1018:"6ba7190d",1054:"cda40cae",1098:"cfa2de30",1107:"eeabb12f",1199:"b85648e1",1235:"4971822d",1264:"10d4f0d3",1400:"185d0923",1475:"51b093f2",1500:"d08b03b3",1558:"446f3666",1567:"8c39a7a2",1715:"9d935eb6",1850:"d1b7c7db",2055:"0fdedfc5",2061:"7da5e510",2076:"d4b64326",2078:"62b38600",2085:"192b6a61",2360:"65be7d01",2373:"b46c3eaf",2584:"8eaca8eb",2586:"b809fc57",2631:"7d84a9e9",2633:"191788f5",2777:"c103cf90",2786:"9be07523",2835:"ea7a941e",2845:"d14041c3",2853:"210d2f40",2966:"565789dc",3015:"9966ef6e",3021:"cfa4288f",3129:"21118c24",3148:"6a7491bc",3185:"f4b12fd2",3321:"694f423a",3325:"d38c8ec6",3469:"83202793",3483:"fadf8c1b",3495:"75e20d36",3588:"0fc762c0",3595:"40f8ed4e",3633:"7e369a0e",3779:"0b7faa87",3822:"c192f1b9",3852:"ab22b4f7",3881:"2bec61c8",3916:"3fc1a455",3949:"f460ed60",3988:"11dfbd63",3997:"08b60ae4",4013:"c81ff6e0",4027:"36358d72",4028:"1d9003e1",4132:"eec55c75",4134:"9cd944e0",4141:"58a5fe1a",4142:"5238f25d",4166:"49ff7265",4194:"63e95596",4202:"57807487",4250:"a201ce1c",4291:"9c7472fc",4424:"d8e65e8a",4475:"d7543fa1",4557:"114db760",4618:"45da1980",4630:"18649363",4812:"f20d897d",4853:"f7338c91",4886:"6302952f",4889:"bb23a7bd",4920:"b67e5d15",4962:"afadf186",4970:"55528f37",4989:"9c93875e",5101:"fc29e504",5122:"ee7a4ff8",5123:"4c817e3a",5219:"cfd81712",5265:"8a792a52",5272:"d7e82ca5",5320:"67a503bd",5335:"d77e48f5",5350:"4cd2a2af",5353:"4a9061e5",5423:"2ac15d82",5448:"63b3f973",5504:"b9ffa436",5694:"44a7d1df",5740:"5798b933",5742:"6f50d3cb",5832:"29ea6eee",5852:"10cefecb",5861:"e414ae82",5866:"746cfe2d",5943:"0ded4df5",5966:"b03616ab",6061:"825d8c20",6115:"94a862cc",6287:"373290c4",6355:"3962d1bb",6386:"5d703faa",6422:"d52978b6",6465:"82c0454b",6491:"41585657",6543:"733abe4c",6584:"bcb8c55f",6616:"ac267d98",6672:"9c7472fc",6717:"49b70407",6723:"941200b7",6724:"b4905e08",6797:"bcf6ccf6",6861:"7a11f993",6866:"4ee7b6e5",6953:"8572a44b",6998:"35539cef",7098:"992c4a90",7149:"f6f58d32",7249:"2dbabe8c",7277:"6e389211",7320:"9ff45764",7396:"b97d027a",7408:"db294439",7443:"f0d813bb",7498:"93b045aa",7513:"0b4554ce",7537:"eee766f7",7700:"3170bb72",7722:"d7a9ad86",7793:"b6848169",7817:"3c4d7aab",7836:"82638c55",7853:"95750a0a",8191:"fb0c28b5",8318:"922f3eee",8382:"2e976381",8401:"476362fb",8413:"7448d5c4",8545:"8fe42f95",8588:"bd262216",8674:"bade954e",8715:"269bc776",8744:"a530f746",8760:"3011bc9b",8845:"9c10cfb5",8897:"8210c243",8907:"eaabf3d6",8926:"2284fc7c",9048:"13c96c74",9111:"99194259",9146:"b5bc0bab",9167:"8e3aa667",9187:"d599c63b",9192:"0f37c11a",9257:"87a88033",9293:"098c020a",9408:"8659aa3b",9460:"c98a88e4",9515:"f4614fb9",9548:"5245b2b7",9591:"fec476c6",9592:"bd5b1cc8",9594:"86580fca",9647:"e42e7000",9743:"ef424470",9772:"24eaace4",9796:"0c53f212",9824:"c1e700b0",9850:"34d66cab",9881:"d455b0a0",9997:"9df61a79"}[e]+".js",t.miniCssF=e=>{},t.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),f={},b="rxdb:",t.l=(e,a,d,c)=>{if(f[e])f[e].push(a);else{var r,o;if(void 0!==d)for(var n=document.getElementsByTagName("script"),i=0;i{r.onerror=r.onload=null,clearTimeout(s);var b=f[e];if(delete f[e],r.parentNode&&r.parentNode.removeChild(r),b&&b.forEach(e=>e(d)),a)return a(d)},s=setTimeout(u.bind(null,void 0,{type:"timeout",target:r}),12e4);r.onerror=u.bind(null,r.onerror),r.onload=u.bind(null,r.onload),o&&document.head.appendChild(r)}},t.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.p="/",t.gca=function(e){return e={17896441:"8401",36715375:"2076",37055470:"4970",51038524:"4889",51334108:"8926",58215328:"5353",61792630:"7277",98405524:"5504","5134b15f":"10","280a2389":"176","3ebfb37f":"205","25626d15":"268","38a45a95":"405","4616b86a":"465",f44bb875:"561","84ae55a4":"588",b672caf7:"813",c0f75fb9:"833","15f1e21f":"970",fe2a63b2:"1018","8a442806":"1054",a7f10198:"1098",b0889a22:"1107","4ed9495b":"1199",a7456010:"1235","6fa8aa1a":"1264",d4da9db3:"1400","0f6e10f0":"1475",a406dc27:"1500",f43e80a8:"1558","22dd74f7":"1567","9dd8ea89":"1715","8bc07e20":"1850","26b8a621":"2055","380cc66a":"2061","38bbf12a":"2078",fe7a07ee:"2085",e6b4453d:"2360","4f17bbdd":"2373","0e268d20":"2584",d2758528:"2586","8bfd920a":"2631","90102fdf":"2633",c4de80f8:"2777","86b4e356":"2786","0b761dc7":"2835",d622bd51:"2845",d04a108d:"2853","2456d5e0":"2966","85caacef":"3015","9e91b6f0":"3021","1b0f8c91":"3129","39600c95":"3148","714575d7":"3185",ae2c2832:"3321","9dae6e71":"3325",ed2d6610:"3469","13dc6548":"3483","7bbb96fd":"3495",b2653a00:"3588","931f4566":"3595","5b5afcec":"3633",b30f4f1f:"3779","8070e160":"3822",cbbe8f0a:"3852","5a273530":"3881","08ff000c":"3916","77d975e6":"3949",eb2e4b0c:"3988","68a466be":"3997","1b238727":"4013","8b4bf532":"4027",ebace26e:"4028","2efd0200":"4132","393be207":"4134",c6fdd490:"4141","2c41656d":"4142","92698a99":"4166",de9a3c97:"4194","91b454ee":"4202","432b83f9":"4424","045bd6f5":"4475","8b0a0922":"4557","6fd28feb":"4618",f15938da:"4630","25a43fd4":"4812","326aca46":"4853",eadd9b3c:"4886",feac4174:"4920",dc42ba65:"4962","0e467ee2":"4989","2fe9ecb2":"5101","924d6dd6":"5122",d20e74b4:"5123","401008a8":"5219",e8a836f3:"5265","118cde4c":"5272","6ae3580c":"5320","7815dd0c":"5335","3417a9c7":"5350","01a106d5":"5423",f61fdf57:"5448","21fa2740":"5694",c6349bb6:"5740",aba21aa0:"5742","34f94d1b":"5832",bdd39edd:"5852","77979bef":"5861","6187b59a":"5866","41f941a1":"5966","1f391b9e":"6061","951d0efb":"6115",cde77f4f:"6287",b8c49ce4:"6355","4777fd9a":"6386","6bfb0089":"6422","03e37916":"6465",ab919a1f:"6491",dbde2ffe:"6543",c44853e1:"6584","01684a0a":"6616","55a5b596":"6717","8aa53ed7":"6723","2564bf4f":"6724",e24529eb:"6797","84a3af36":"6861","187b985e":"6866","1c0701dd":"6953",ee1b9f21:"6998",a7bd4aaa:"7098",a574e172:"7149",c9c8e0b6:"7249",db34d6b0:"7320",ec526260:"7396","6cbff7c2":"7408","2aec6989":"7498",f14ec96f:"7513","0596642b":"7537","8489a755":"7700",ff492cda:"7722",c7b47308:"7793","502d8946":"7817","820807a1":"7836","35e4d287":"7853","32667c41":"8191",badcd764:"8318","0027230a":"8382","1db64337":"8413","4af60d2e":"8545","1e0353aa":"8588",ad16b3ea:"8674","597d88be":"8715","294ac9d5":"8744",a442adcd:"8760",f490b64c:"8845","11d75f9a":"8897",f1c185f0:"8907",a94703ab:"9048","1b5fa8ad":"9111",c843a053:"9146",c3bc9c50:"9167",ac62b32d:"9192","8a22f3a9":"9257",a69eebfc:"9408","51014a8a":"9460","8084fe3b":"9515","4adf80bb":"9548","4ba7e5a3":"9591","7f02c700":"9592","40b6398a":"9594","5e95c892":"9647","1da545ff":"9743","14d72841":"9772","0e945c41":"9796",aa14e6b1:"9824","8288c265":"9881","8bc82b1f":"9997"}[e]||e,t.p+t.u(e)},(()=>{var e={5354:0,1869:0};t.f.j=(a,d)=>{var f=t.o(e,a)?e[a]:void 0;if(0!==f)if(f)d.push(f[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var b=new Promise((d,b)=>f=e[a]=[d,b]);d.push(f[2]=b);var c=t.p+t.u(a),r=new Error;t.l(c,d=>{if(t.o(e,a)&&(0!==(f=e[a])&&(e[a]=void 0),f)){var b=d&&("load"===d.type?"missing":d.type),c=d&&d.target&&d.target.src;r.message="Loading chunk "+a+" failed.\n("+b+": "+c+")",r.name="ChunkLoadError",r.type=b,r.request=c,f[1](r)}},"chunk-"+a,a)}},t.O.j=a=>0===e[a];var a=(a,d)=>{var f,b,[c,r,o]=d,n=0;if(c.some(a=>0!==e[a])){for(f in r)t.o(r,f)&&(t.m[f]=r[f]);if(o)var i=o(t)}for(a&&a(d);n
-
-
-
-
-Backup | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
With the backup plugin you can write the current database state and ongoing changes into folders on the filesystem.
-The files are written in plain json together with their attachments so that you can read them out with any software or tools, without being bound to RxDB.
-
This is useful to:
-
-
Consume the database content with other software that cannot replicate with RxDB
-
Write a backup of the database to a remote server by mounting the backup folder on the other server.
-
-
The backup plugin works only in node.js, not in a browser. It is intended to have a backup strategy when using RxDB on the server side like with the RxServer. To run backups on the client side, you should use one of the replication plugins instead.
Write the whole database to the filesystem once.
-When called multiple times, it will continue from the last checkpoint and not start all over again.
-
const backupOptions = {
- // if false, a one-time backup will be written
- live: false,
- // the folder where the backup will be stored
- directory: '/my-backup-folder/',
- // if true, attachments will also be saved
- attachments: true
-}
-const backupState = myDatabase.backup(backupOptions);
-await backupState.awaitInitialBackup();
-
-// call again to run from the last checkpoint
-const backupState2 = myDatabase.backup(backupOptions);
-await backupState2.awaitInitialBackup();
When live: true is set, the backup will write all ongoing changes to the backup directory.
-
const backupOptions = {
- // set live: true to have an ongoing backup
- live: true,
- directory: '/my-backup-folder/',
- attachments: true
-}
-const backupState = myDatabase.backup(backupOptions);
-
-// you can still await the initial backup write, but further changes will still be processed.
-await backupState.awaitInitialBackup();
-
-
\ No newline at end of file
diff --git a/docs/backup.md b/docs/backup.md
deleted file mode 100644
index 669bd5ca288..00000000000
--- a/docs/backup.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Backup
-
-> Easily back up your RxDB database to JSON files and attachments on the filesystem with the Backup Plugin - ensuring reliable Node.js data protection.
-
-# 📥 Backup Plugin
-
-With the backup plugin you can write the current database state and ongoing changes into folders on the filesystem.
-The files are written in plain json together with their attachments so that you can read them out with any software or tools, without being bound to RxDB.
-
-This is useful to:
- - Consume the database content with other software that cannot replicate with RxDB
- - Write a backup of the database to a remote server by mounting the backup folder on the other server.
-
-The backup plugin works only in node.js, not in a browser. It is intended to have a backup strategy when using RxDB on the server side like with the [RxServer](./rx-server.md). To run backups on the client side, you should use one of the [replication](./replication.md) plugins instead.
-
-## Installation
-
-```javascript
-import { addRxPlugin } from 'rxdb';
-import { RxDBBackupPlugin } from 'rxdb/plugins/backup';
-addRxPlugin(RxDBBackupPlugin);
-```
-
-## one-time backup
-
-Write the whole database to the filesystem **once**.
-When called multiple times, it will continue from the last checkpoint and not start all over again.
-
-```javascript
-const backupOptions = {
- // if false, a one-time backup will be written
- live: false,
- // the folder where the backup will be stored
- directory: '/my-backup-folder/',
- // if true, attachments will also be saved
- attachments: true
-}
-const backupState = myDatabase.backup(backupOptions);
-await backupState.awaitInitialBackup();
-
-// call again to run from the last checkpoint
-const backupState2 = myDatabase.backup(backupOptions);
-await backupState2.awaitInitialBackup();
-```
-
-## live backup
-
-When `live: true` is set, the backup will write all ongoing changes to the backup directory.
-
-```javascript
-const backupOptions = {
- // set live: true to have an ongoing backup
- live: true,
- directory: '/my-backup-folder/',
- attachments: true
-}
-const backupState = myDatabase.backup(backupOptions);
-
-// you can still await the initial backup write, but further changes will still be processed.
-await backupState.awaitInitialBackup();
-```
-
-## writeEvents$
-
-You can listen to the `writeEvents$` Observable to get notified about written backup files.
-
-```javascript
-const backupOptions = {
- live: false,
- directory: '/my-backup-folder/',
- attachments: true
-}
-const backupState = myDatabase.backup(backupOptions);
-
-const subscription = backupState.writeEvents$.subscribe(writeEvent => console.dir(writeEvent));
-/*
-> {
- collectionName: 'humans',
- documentId: 'foobar',
- files: [
- '/my-backup-folder/foobar/document.json'
- ],
- deleted: false
-}
-*/
-```
-
-## Limitations
-
-- It is currently not possible to import from a written backup. If you need this functionality, please make a pull request.
diff --git a/docs/capacitor-database.html b/docs/capacitor-database.html
deleted file mode 100644
index eee23058345..00000000000
--- a/docs/capacitor-database.html
+++ /dev/null
@@ -1,161 +0,0 @@
-
-
-
-
-
-Capacitor Database Guide - SQLite, RxDB & More | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Capacitor is an open source native JavaScript runtime to build Web based Native apps. You can use it to create cross-platform iOS, Android, and Progressive Web Apps with the web technologies JavaScript, HTML, and CSS.
-It is developed by the Ionic Team and provides a great alternative to create hybrid apps. Compared to React Native, Capacitor is more Web-Like because the JavaScript runtime supports most Web APIs like IndexedDB, fetch, and so on.
-
To read and write persistent data in Capacitor, there are multiple solutions which are shown in the following.
Capacitor comes with a native Preferences API which is a simple, persistent key->value store for lightweight data, similar to the browsers localstorage or React Native AsyncStorage.
-
To use it, you first have to install it from npm npm install @capacitor/preferences and then you can import it and write/read data.
-Notice that all calls to the preferences API are asynchronous so they return a Promise that must be await-ed.
The preferences API is good when only a small amount of data needs to be stored and when no query capabilities besides the key access are required. Complex queries or other features like indexes or replication are not supported which makes the preferences API not suitable for anything more than storing simple data like user settings.
Since Capacitor apps run in a web view, Web APIs like IndexedDB, Localstorage and WebSQL are available. But the default browser behavior is to clean up these storages regularly when they are not in use for a long time or the device is low on space. Therefore you cannot 100% rely on the persistence of the stored data and your application needs to expect that the data will be lost eventually.
-
Storing data in these storages can be done in browsers, because there is no other option. But in Capacitor iOS and Android, you should not rely on these.
SQLite is a SQL based relational database written in C that was crafted to be embed inside of applications. Operations are written in the SQL query language and SQLite generally follows the PostgreSQL syntax.
-
To use SQLite in Capacitor, there are three options:
It is recommended to use the @capacitor-community/sqlite because it has the best maintenance and is open source. Install it first npm install --save @capacitor-community/sqlite and then set the storage location for iOS apps:
The downside of SQLite is that it is lacking many features that are handful when using a database together with an UI based application like your Capacitor app. For example it is not possible to observe queries or document fields. Also there is no realtime replication feature, you can only import json files. This makes SQLite a good solution when you just want to store data on the client, but when you want to sync data with a server or other clients or create big complex realtime applications, you have to use something else.
RxDB is an local first, NoSQL database for JavaScript Applications like hybrid apps. Because it is reactive, you can subscribe to all state changes like the result of a query or even a single field of a document. This is great for UI-based realtime applications in a way that makes it easy to develop realtime applications like what you need in Capacitor.
-
Because RxDB is made for Web applications, most of the available RxStorage plugins can be used to store and query data in a Capacitor app. However it is recommended to use the SQLite RxStorage because it stores the data on the filesystem of the device, not in the JavaScript runtime (like IndexedDB). Storing data on the filesystem ensures it is persistent and will not be cleaned up by any process. Also the performance of SQLite is much faster compared to IndexedDB, because SQLite does not have to go through a browsers permission layers. For the SQLite binding you should use the @capacitor-community/sqlite package.
-
Because the SQLite RxStorage is part of the 👑 Premium Plugins which must be purchased, it is recommended to use the LocalStorage RxStorage while testing and prototyping your Capacitor app.
-
To use the SQLite RxStorage in Capacitor you have to install all dependencies via npm install rxdb rxjs rxdb-premium @capacitor-community/sqlite.
-
For iOS apps you should add a database location in your Capacitor settings:
-
-
\ No newline at end of file
diff --git a/docs/capacitor-database.md b/docs/capacitor-database.md
deleted file mode 100644
index 149fdbe291e..00000000000
--- a/docs/capacitor-database.md
+++ /dev/null
@@ -1,243 +0,0 @@
-# Capacitor Database Guide - SQLite, RxDB & More
-
-> Explore Capacitor's top data storage solutions - from key-value to real-time databases. Compare SQLite, RxDB, and more in this in-depth guide.
-
-import {Tabs} from '@site/src/components/tabs';
-import {Steps} from '@site/src/components/steps';
-
-# Capacitor Database - SQLite, RxDB and others
-
-[Capacitor](https://capacitorjs.com/) is an open source native JavaScript runtime to build Web based Native apps. You can use it to create cross-platform iOS, Android, and Progressive Web Apps with the web technologies JavaScript, HTML, and CSS.
-It is developed by the Ionic Team and provides a great alternative to create hybrid apps. Compared to [React Native](./react-native-database.md), Capacitor is more Web-Like because the JavaScript runtime supports most Web APIs like IndexedDB, fetch, and so on.
-
-To read and write persistent data in Capacitor, there are multiple solutions which are shown in the following.
-
-
-
-## Database Solutions for Capacitor
-
-### Preferences API
-
-Capacitor comes with a native [Preferences API](https://capacitorjs.com/docs/apis/preferences) which is a simple, persistent key->value store for lightweight data, similar to the browsers localstorage or React Native [AsyncStorage](./react-native-database.md#asyncstorage).
-
-To use it, you first have to install it from npm `npm install @capacitor/preferences` and then you can import it and write/read data.
-Notice that all calls to the preferences API are asynchronous so they return a `Promise` that must be `await`-ed.
-
-```ts
-import { Preferences } from '@capacitor/preferences';
-
-// write
-await Preferences.set({
- key: 'foo',
- value: 'baar',
-});
-
-// read
-const { value } = await Preferences.get({ key: 'foo' }); // > 'bar'
-
-// delete
-await Preferences.remove({ key: 'foo' });
-```
-
-The preferences API is good when only a small amount of data needs to be stored and when no query capabilities besides the key access are required. Complex queries or other features like indexes or replication are not supported which makes the preferences API not suitable for anything more than storing simple data like user settings.
-
-### Localstorage/IndexedDB/WebSQL
-
-Since Capacitor apps run in a web view, Web APIs like IndexedDB, [Localstorage](./articles/localstorage.md) and WebSQL are available. But the default browser behavior is to clean up these storages regularly when they are not in use for a long time or the device is low on space. Therefore you cannot 100% rely on the persistence of the stored data and your application needs to expect that the data will be lost eventually.
-
-Storing data in these storages can be done in browsers, because there is no other option. But in Capacitor iOS and Android, you should not rely on these.
-
-### SQLite
-
-SQLite is a SQL based relational database written in C that was crafted to be embed inside of applications. Operations are written in the SQL query language and SQLite generally follows the PostgreSQL syntax.
-
-To use SQLite in Capacitor, there are three options:
-
-- The [@capacitor-community/sqlite](https://github.com/capacitor-community/sqlite) package
-- The [cordova-sqlite-storage](https://github.com/storesafe/cordova-sqlite-storage) package
-- The non-free [Ionic](./articles/ionic-database.md) [Secure Storage](https://ionic.io/products/secure-storage) which comes at **999$** per month.
-
-It is recommended to use the `@capacitor-community/sqlite` because it has the best maintenance and is open source. Install it first `npm install --save @capacitor-community/sqlite` and then set the storage location for iOS apps:
-
-```json
-{
- "plugins": {
- "CapacitorSQLite": {
- "iosDatabaseLocation": "Library/CapacitorDatabase"
- }
- }
-}
-```
-
-Now you can create a database connection and use the SQLite database.
-
-```ts
-import { Capacitor } from '@capacitor/core';
-import {
- CapacitorSQLite, SQLiteDBConnection, SQLiteConnection, capSQLiteSet,
- capSQLiteChanges, capSQLiteValues, capEchoResult, capSQLiteResult,
- capNCDatabasePathResult
-} from '@capacitor-community/sqlite';
-
-const sqlite = new SQLiteConnection(CapacitorSQLite);
-const database: SQLiteDBConnection = await this.sqlite.createConnection(
- databaseName,
- encrypted,
- mode,
- version,
- readOnly
-);
-let { rows } = database.query('SELECT somevalue FROM sometable');
-```
-
-The downside of SQLite is that it is lacking many features that are handful when using a database together with an UI based application like your Capacitor app. For example it is not possible to observe queries or document fields. Also there is no realtime replication feature, you can only import json files. This makes SQLite a good solution when you just want to store data on the client, but when you want to sync data with a server or other clients or create big complex realtime applications, you have to use something else.
-
-### RxDB
-
-
-
-[RxDB](https://rxdb.info/) is an local first, NoSQL database for JavaScript Applications like hybrid apps. Because it is reactive, you can subscribe to all state changes like the result of a query or even a single field of a document. This is great for UI-based realtime applications in a way that makes it easy to develop realtime applications like what you need in Capacitor.
-
-Because RxDB is made for Web applications, most of the [available RxStorage](./rx-storage.md) plugins can be used to store and query data in a Capacitor app. However it is recommended to use the [SQLite RxStorage](./rx-storage-sqlite.md) because it stores the data on the filesystem of the device, not in the JavaScript runtime (like IndexedDB). Storing data on the filesystem ensures it is persistent and will not be cleaned up by any process. Also the performance of SQLite is [much faster](./rx-storage.md#performance-comparison) compared to IndexedDB, because SQLite does not have to go through a browsers permission layers. For the SQLite binding you should use the [@capacitor-community/sqlite](https://github.com/capacitor-community/sqlite) package.
-
-Because the SQLite RxStorage is part of the [👑 Premium Plugins](/premium/) which must be purchased, it is recommended to use the [LocalStorage RxStorage](./rx-storage-localstorage.md) while testing and prototyping your Capacitor app.
-
-To use the SQLite RxStorage in Capacitor you have to install all dependencies via `npm install rxdb rxjs rxdb-premium @capacitor-community/sqlite`.
-
-For iOS apps you should add a database location in your Capacitor settings:
-
-```json
-{
- "plugins": {
- "CapacitorSQLite": {
- "iosDatabaseLocation": "Library/CapacitorDatabase"
- }
- }
-}
-```
-
-Then you can assemble the RxStorage and create a database with it:
-
-
-
-### Import RxDB and SQLite
-
-```ts
-import {
- createRxDatabase
-} from 'rxdb/plugins/core';
-import {
- CapacitorSQLite,
- SQLiteConnection
-} from '@capacitor-community/sqlite';
-import { Capacitor } from '@capacitor/core';
-const sqlite = new SQLiteConnection(CapacitorSQLite);
-```
-
-### Import the RxDB SQLite Storage
-
-
-
-#### RxDB Core
-
-```ts
-import {
- getRxStorageSQLiteTrial,
- getSQLiteBasicsCapacitor
-} from 'rxdb/plugins/storage-sqlite';
-```
-
-#### RxDB Premium 👑
-
-```ts
-import {
- getRxStorageSQLite,
- getSQLiteBasicsCapacitor
-} from 'rxdb-premium/plugins/storage-sqlite';
-```
-
-
-
-### Create a Database with the Storage
-
-
-
-#### RxDB Core
-
-```ts
-// create database
-const myRxDatabase = await createRxDatabase({
- name: 'exampledb',
- storage: getRxStorageSQLiteTrial({
- sqliteBasics: getSQLiteBasicsCapacitor(sqlite, Capacitor)
- })
-});
-```
-
-#### RxDB Premium 👑
-
-```ts
-// create database
-const myRxDatabase = await createRxDatabase({
- name: 'exampledb',
- storage: getRxStorageSQLite({
- sqliteBasics: getSQLiteBasicsCapacitor(sqlite, Capacitor)
- })
-});
-```
-
-
-
-### Add a Collection
-
-```ts
-// create collections
-const collections = await myRxDatabase.addCollections({
- humans: {
- schema: {
- version: 0,
- type: 'object',
- primaryKey: 'id',
- properties: {
- id: { type: 'string', maxLength: 100 },
- name: { type: 'string' },
- age: { type: 'number' }
- },
- required: ['id', 'name']
- }
- }
-});
-```
-
-### Insert a Document
-
-```ts
-await collections.humans.insert({id: 'foo', name: 'bar'});
-```
-
-### Run a Query
-
-```ts
-const result = await collections.humans.find({
- selector: {
- name: 'bar'
- }
-}).exec();
-```
-
-### Observe a Query
-
-```ts
-await collections.humans.find({
- selector: {
- name: 'bar'
- }
-}).$.subscribe(result => {/* ... */});
-```
-
-
-
-## Follow up
-
-- If you haven't done yet, you should start learning about RxDB with the [Quickstart Tutorial](./quickstart.md).
-- There is a followup list of other [client side database alternatives](./alternatives.md).
diff --git a/docs/chat/index.html b/docs/chat/index.html
deleted file mode 100644
index 24933bd33cd..00000000000
--- a/docs/chat/index.html
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-Chat - RxDB - JavaScript Database | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
To make the replication work, and for other reasons, RxDB has to keep deleted documents in storage so that it can replicate their deletion state.
-This ensures that when a client is offline, the deletion state is still known and can be replicated with the backend when the client goes online again.
-
Keeping too many deleted documents in the storage, can slow down queries or fill up too much disc space.
-With the cleanup plugin, RxDB will run cleanup cycles that clean up deleted documents when it can be done safely.
You can set a specific cleanup policy when a RxDatabase is created. For most use cases, the defaults should be ok.
-
import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-const db = await createRxDatabase({
- name: 'heroesdb',
- storage: getRxStorageLocalstorage(),
- cleanupPolicy: {
- /**
- * The minimum time in milliseconds for how long
- * a document has to be deleted before it is
- * purged by the cleanup.
- * [default=one month]
- */
- minimumDeletedTime: 1000 * 60 * 60 * 24 * 31, // one month,
- /**
- * The minimum amount of that that the RxCollection must have existed.
- * This ensures that at the initial page load, more important
- * tasks are not slowed down because a cleanup process is running.
- * [default=60 seconds]
- */
- minimumCollectionAge: 1000 * 60, // 60 seconds
- /**
- * After the initial cleanup is done,
- * a new cleanup is started after [runEach] milliseconds
- * [default=5 minutes]
- */
- runEach: 1000 * 60 * 5, // 5 minutes
- /**
- * If set to true,
- * RxDB will await all running replications
- * to not have a replication cycle running.
- * This ensures we do not remove deleted documents
- * when they might not have already been replicated.
- * [default=true]
- */
- awaitReplicationsInSync: true,
- /**
- * If true, it will only start the cleanup
- * when the current instance is also the leader.
- * This ensures that when RxDB is used in multiInstance mode,
- * only one instance will start the cleanup.
- * [default=true]
- */
- waitForLeadership: true
- }
-});
You can manually run a cleanup per collection by calling RxCollection.cleanup().
-
-/**
- * Manually run the cleanup with the
- * minimumDeletedTime from the cleanupPolicy.
- */
-await myRxCollection.cleanup();
-
-
-/**
- * Overwrite the minimumDeletedTime
- * be setting it explicitly (time in milliseconds)
- */
-await myRxCollection.cleanup(1000);
-
-/**
- * Purge all deleted documents no
- * matter when they where deleted
- * by setting minimumDeletedTime to zero.
- */
-await myRxCollection.cleanup(0);
When you have a collection with documents and you want to empty it by purging all documents, the recommended way is to call myRxCollection.remove(). However, this will destroy the JavaScript class of the collection and stop all listeners and observables.
-Sometimes the better option might be to manually delete all documents and then use the cleanup plugin to purge the deleted documents:
-
// delete all documents
-await myRxCollection.find().remove();
-// purge all deleted documents
-await myRxCollection.cleanup(0);
The cleanup cycles are optimized to run only when the database is idle and it is unlikely that another database interaction performance will be decreased in the meantime. For example, by default, the cleanup does not run in the first 60 seconds of the creation of a collection to ensure the initial page load of your website does not slow down. Also, we use mechanisms like the requestIdleCallback() API to improve the correct timing of the cleanup cycle.
-
-
\ No newline at end of file
diff --git a/docs/cleanup.md b/docs/cleanup.md
deleted file mode 100644
index d23f1505247..00000000000
--- a/docs/cleanup.md
+++ /dev/null
@@ -1,118 +0,0 @@
-# Cleanup
-
-> Optimize storage and speed up queries with RxDB's Cleanup Plugin, automatically removing old deleted docs while preserving replication states.
-
-# 🧹 Cleanup
-
-To make the replication work, and for other reasons, RxDB has to keep deleted documents in storage so that it can replicate their deletion state.
-This ensures that when a client is offline, the deletion state is still known and can be replicated with the backend when the client goes online again.
-
-Keeping too many deleted documents in the storage, can slow down queries or fill up too much disc space.
-With the cleanup plugin, RxDB will run cleanup cycles that clean up deleted documents when it can be done safely.
-
-## Installation
-
-```ts
-import { addRxPlugin } from 'rxdb';
-import { RxDBCleanupPlugin } from 'rxdb/plugins/cleanup';
-addRxPlugin(RxDBCleanupPlugin);
-```
-
-## Create a database with cleanup options
-
-You can set a specific cleanup policy when a `RxDatabase` is created. For most use cases, the defaults should be ok.
-
-```ts
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-const db = await createRxDatabase({
- name: 'heroesdb',
- storage: getRxStorageLocalstorage(),
- cleanupPolicy: {
- /**
- * The minimum time in milliseconds for how long
- * a document has to be deleted before it is
- * purged by the cleanup.
- * [default=one month]
- */
- minimumDeletedTime: 1000 * 60 * 60 * 24 * 31, // one month,
- /**
- * The minimum amount of that that the RxCollection must have existed.
- * This ensures that at the initial page load, more important
- * tasks are not slowed down because a cleanup process is running.
- * [default=60 seconds]
- */
- minimumCollectionAge: 1000 * 60, // 60 seconds
- /**
- * After the initial cleanup is done,
- * a new cleanup is started after [runEach] milliseconds
- * [default=5 minutes]
- */
- runEach: 1000 * 60 * 5, // 5 minutes
- /**
- * If set to true,
- * RxDB will await all running replications
- * to not have a replication cycle running.
- * This ensures we do not remove deleted documents
- * when they might not have already been replicated.
- * [default=true]
- */
- awaitReplicationsInSync: true,
- /**
- * If true, it will only start the cleanup
- * when the current instance is also the leader.
- * This ensures that when RxDB is used in multiInstance mode,
- * only one instance will start the cleanup.
- * [default=true]
- */
- waitForLeadership: true
- }
-});
-```
-
-## Calling cleanup manually
-
-You can manually run a cleanup per collection by calling `RxCollection.cleanup()`.
-
-```ts
-
-/**
- * Manually run the cleanup with the
- * minimumDeletedTime from the cleanupPolicy.
- */
-await myRxCollection.cleanup();
-
-/**
- * Overwrite the minimumDeletedTime
- * be setting it explicitly (time in milliseconds)
- */
-await myRxCollection.cleanup(1000);
-
-/**
- * Purge all deleted documents no
- * matter when they where deleted
- * by setting minimumDeletedTime to zero.
- */
-await myRxCollection.cleanup(0);
-```
-
-## Using the cleanup plugin to empty a collection
-
-When you have a collection with documents and you want to empty it by purging all documents, the recommended way is to call `myRxCollection.remove()`. However, this will destroy the JavaScript class of the collection and stop all listeners and observables.
-Sometimes the better option might be to manually delete all documents and then use the cleanup plugin to purge the deleted documents:
-
-```ts
-// delete all documents
-await myRxCollection.find().remove();
-// purge all deleted documents
-await myRxCollection.cleanup(0);
-```
-
-## FAQ
-
-
- When does the cleanup run
-
- The cleanup cycles are optimized to run only when the database is idle and it is unlikely that another database interaction performance will be decreased in the meantime. For example, by default, the cleanup does not run in the first 60 seconds of the creation of a collection to ensure the initial page load of your website does not slow down. Also, we use mechanisms like the `requestIdleCallback()` API to improve the correct timing of the cleanup cycle.
-
-
diff --git a/docs/code/index.html b/docs/code/index.html
deleted file mode 100644
index 7e40caed251..00000000000
--- a/docs/code/index.html
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-Code - RxDB - JavaScript Database | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The RxDB core maintainer has the answers. Schedule a compact consultancy session for quick fixes and suggestions on how you should use RxDB or related technologies.
Our trusted partners handle the full development and implementation of your local JavaScript database solution. You can relax while they bring your vision to life.
Get started
1
Share your project
Tell us what you're aiming to achieve and what technical details matter most, so we can get a clear picture of your requirements.
2
Get paired
We'll match you with a reliable RxDB expert or partner whose skills fit your project's needs.
3
Build and collaborate
Your partner will team up with you to create, refine, and deliver the solution you’re looking for.
Become a Partner?
Become part of our partner network for freelance developers and agencies. Get in touch with clients seeking skilled experts to build their apps based on RxDB.
Apply as a partner
-
-
\ No newline at end of file
diff --git a/docs/contribute.md b/docs/contribute.md
deleted file mode 100644
index b04d5ed0255..00000000000
--- a/docs/contribute.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Contribute
-
-> Got a fix or fresh idea? Learn how to contribute to RxDB, run tests, and shape the future of this cutting-edge NoSQL database for JavaScript.
-
-# Contribution
-
-We are open to, and grateful for, any contributions made by the community.
-
-# Developing
-
-## Requirements
-
-Before you can start developing, do the following:
-
-1. Make sure you have installed nodejs with the version stated in the [.nvmrc](https://github.com/pubkey/rxdb/blob/master/.nvmrc)
-2. Clone the repository `git clone https://github.com/pubkey/rxdb.git`
-3. Install the dependencies `cd rxdb && npm install`
-4. Make sure that the tests work for you. At first, try it out with `npm run test:node:memory` which tests the memory storage in node. In the [package.json](https://github.com/pubkey/rxdb/blob/master/package.json) you can find more scripts to run the tests with different storages.
-
-## Adding tests
-
-Before you start creating a bugfix or a feature, you should create a test to reproduce it. Tests are in the `test/unit`-folder.
-If you want to reproduce a bug, you can modify the test in [this file](https://github.com/pubkey/rxdb/blob/master/test/unit/bug-report.test.ts).
-
-## Making a PR
-
-If you make a pull-request, ensure the following:
-
-1. Every feature or bugfix must be committed together with a unit-test which ensures everything works as expected.
-2. Do not commit build-files (anything in the `dist`-folder)
-3. Before you add non-trivial changes, create an issue to discuss if this will be merged and you don't waste your time.
-4. To run the unit and integration-tests, do `npm run test` and ensure everything works as expected
-
-## Getting help
-
-If you need help with your contribution, ask at [discord](https://rxdb.info/chat/).
-
-## No-Go
-
-When reporting a bug, you need to make a PR with a test case that runs in the CI and reproduces your problem.
-Sending a link with a repo does not help the maintainer because installing random peoples projects is time consuming and dangerous.
-Also the maintainer will never go on a bug hunt based on your plain description. Either you report the bug with a test case, or the maintainer will likely not help you.
-
-# Docs
-
-The source of the documentation is at the `docs-src`-folder.
-To read the docs locally, run `npm run docs:install && npm run docs:serve` and open `http://localhost:4000/`
-
-# Thank you for contributing!
diff --git a/docs/contribution.html b/docs/contribution.html
deleted file mode 100644
index c3ed1f1e062..00000000000
--- a/docs/contribution.html
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-Contribute | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Before you can start developing, do the following:
-
-
Make sure you have installed nodejs with the version stated in the .nvmrc
-
Clone the repository git clone https://github.com/pubkey/rxdb.git
-
Install the dependencies cd rxdb && npm install
-
Make sure that the tests work for you. At first, try it out with npm run test:node:memory which tests the memory storage in node. In the package.json you can find more scripts to run the tests with different storages.
Before you start creating a bugfix or a feature, you should create a test to reproduce it. Tests are in the test/unit-folder.
-If you want to reproduce a bug, you can modify the test in this file.
When reporting a bug, you need to make a PR with a test case that runs in the CI and reproduces your problem.
-Sending a link with a repo does not help the maintainer because installing random peoples projects is time consuming and dangerous.
-Also the maintainer will never go on a bug hunt based on your plain description. Either you report the bug with a test case, or the maintainer will likely not help you.
-
Docs
-
The source of the documentation is at the docs-src-folder.
-To read the docs locally, run npm run docs:install && npm run docs:serve and open http://localhost:4000/
Whenever there are multiple instances in a distributed system, data writes can cause conflicts. Two different clients could do a write to the same document at the same time or while they are both offline. When the clients replicate the document state with the server, a conflict emerges that must be resolved by the system.
-
In RxDB, conflicts are normally resolved by setting a conflictHandler when creating a collection. The conflict handler is a JavaScript function that gets the two conflicting states of the same document and it will return the resolved document state.
-The default conflict handler will always drop the fork state and use the master state to ensure that clients that have been offline for a long time, do not overwrite other clients changes when they go online again.
-
-
With CRDTs (short for Conflict-free replicated data type), all document
-writes are represented as CRDT operations in plain JSON. The CRDT operations are stored together with the document and each time a conflict arises, the CRDT conflict handler will automatically merge the operations in a deterministic way. Using CRDTs is an easy way to "magically" handle all conflict problems in your application by storing the deltas of writes together with the document data.
By default, all CRDTs operations will be run to build the current document state. But in many cases, more granular operations are required to better reflect the desired business logic. For these cases, conditional CRDTs can be used.
-
For example if you have a field points with a maximum of 100, you might want to only run the $inc operation, if the points value is less than 100.
-In an conditional CRDT, you can specify a selector and the operation sets ifMatch and ifNotMatch. At each time the CRDT is applied to the document state, first the selector will run and evaluate which operations path must be used.
-
await myDocument.updateCRDT({
- // only if the selector matches, the ifMatch operation will run
- selector: {
- age: {
- $lt: 100
- }
- },
- // an operation that runs if the selector matches
- ifMatch: {
- $inc: {
- points: 1
- }
- },
- // if the selector does NOT match, you could run a different operation instead
- ifNotMatch: {
- // ...
- }
-});
By default, one CRDT operation is applied to the document in a single database write.
-To represent more complex logic chains, it might make sense to use multiple CRDTs and write them at once inside of a single atomic document write.
-
For these cases, the updateCRDT() method allows to pass an array of operations.
When the same document is inserted in multiple client instances and then replicated, a conflict will emerge and the insert-CRDTs will overwrite each other in a deterministic order.
-You can use insertCRDT() to make conditional insert operations with any logic. To check for the previous existence of a document, use the $exists query operation on the primary key of the document.
-
await myRxCollection.insertCRDT({
- selector: {
- // only run if the document did not exist before.
- id: { $exists: false }
- },
- ifMatch: {
- // if the document did not exist, insert it
- $set: {
- id: 'foo'
- points: 1
- }
- },
- ifNotMatch: {
- // if document existed already, increment the points by +1
- $inc: {
- points: 1
- }
- }
-});
You can delete a document with a CRDT operation by setting _deleted to true. Calling RxDocument.remove() will do exactly the same when CRDTs are activated.
CRDT operations are stored inside of a special field besides your 'normal' document fields.
-When replicating document data with the RxDB replication or the CouchDB replication or even any custom replication, the CRDT operations must be replicated together with the document data as if they would be 'normal' a document property.
-
When any instances makes a write to the document, it is required to update the CRDT operations accordingly. For example if your custom backend updates a document, it must also do that by adding a CRDT operation. In dev-mode RxDB will refuse to store any document data where the document properties do not match the result of the CRDT operations.
There are already CRDT libraries out there that have been considered to be used with RxDB. The biggest ones are automerge and yjs. The decision was made to not use these but instead go for a more NoSQL way of designing the CRDT format because:
-
-
Users do not have to learn a new syntax but instead can use the NoSQL query operations which they already know to manipulate the JSON data of a document.
-
RxDB is often used to replicate data with any custom backend on an already existing infrastructure. Using NoSQL operators instead of binary data in CRDTs, makes it easy to implement the exact same logic on these backends so that the backend can also do document writes and still be compliant to the RxDB CRDT plugin.
-
-
So instead of using YJS or Automerge with a database, you can use RxDB with the CRDT plugin to have a more database specific CRDT approach. This gives you additional features for free such as schema validation or data migration.
CRDT can only be use when your business logic allows to represent document changes via static json operators.
-If you can have cases where user interaction is required to correctly merge conflicting document states, you cannot use CRDTs for that.
-
Also when CRDTs are used, it is no longer allowed to do non-CRDT writes to the document properties.
While the CRDT plugin can automatically merge concurrent document updates, it is not the only way to resolve conflicts in RxDB.
-An alternative approach to CRDT is to use RxDB's built-in conflict handling system.
-
-
Why use conflict handlers instead of CRDT?
-
-
Conflict handlers offer a simpler and more flexible way to manage data conflicts. Instead of encoding changes as CRDT operations, you define how RxDB should decide which document version "wins" with plain JavaScript code. This approach is easier to reason about because it works directly with your domain logic. For example, you can compare timestamps, prioritize certain fields, or even involve user interaction to resolve conflicts.
-
Conflict handlers are:
-
-
Easier to understand: you work with plain document states instead of CRDT operations.
-
Fully customizable: you can define any merge strategy, from simple last-write-wins to complex rule-based logic.
-
Compatible with all data types: unlike CRDTs, which are best suited for numeric or set-based updates.
-
Transparent: you always know which state is being written and why.
Your data model includes contextual or user-specific decisions.
-
You prefer a straightforward, rule-based resolution system over automatic merges.
-
-
Use CRDTs if:
-
-
Your app performs frequent offline writes that can be merged deterministically.
-
Your data can be represented as additive, numeric, or array-based updates.
-
You want minimal manual intervention during replication.
-
-
Both methods are first-class citizens in RxDB. CRDTs focus on automatic, deterministic merging, while conflict handlers emphasize clarity, flexibility, and control.
-
Example: merging different fields with conflict handlers instead of CRDT
-
For example, imagine two users edit different fields of the same document at the same time. One updates a name, the other updates a score. A custom conflict handler can merge both changes so no data is lost:
In this example, if the two versions change different properties, the final merged document includes both updates. This kind of logic is often easier to reason about than designing equivalent CRDT operations.
-
-
\ No newline at end of file
diff --git a/docs/crdt.md b/docs/crdt.md
deleted file mode 100644
index 95a3c3bd45b..00000000000
--- a/docs/crdt.md
+++ /dev/null
@@ -1,334 +0,0 @@
-# CRDT - Conflict-free replicated data type Database
-
-> Learn how RxDB's CRDT Plugin resolves document conflicts automatically in distributed systems, ensuring seamless merges and consistent data.
-
-# RxDB CRDT Plugin
-
-Whenever there are multiple instances in a distributed system, data writes can cause conflicts. Two different clients could do a write to the same document at the same time or while they are both offline. When the clients replicate the document state with the server, a conflict emerges that must be resolved by the system.
-
-In [RxDB](./), conflicts are normally resolved by setting a `conflictHandler` when creating a collection. The conflict handler is a JavaScript function that gets the two conflicting states of the same document and it will return the resolved document state.
-The [default conflict handler](./replication.md#conflict-handling) will always drop the fork state and use the master state to ensure that clients that have been offline for a long time, do not overwrite other clients changes when they go online again.
-
-
-
-With CRDTs (short for [Conflict-free replicated data type](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)), all document
-writes are represented as CRDT operations in plain JSON. The CRDT operations are stored together with the document and each time a conflict arises, the CRDT conflict handler will automatically merge the operations in a deterministic way. Using CRDTs is an easy way to "magically" handle all conflict problems in your application by storing the deltas of writes together with the document data.
-
-
-
-## RxDB CRDT operations
-
-In RxDB, a CRDT operation is defined with NoSQL update operators, like you might know them from [MongoDB update operations](https://www.mongodb.com/docs/manual/reference/operator/update/) or the [RxDB update plugin](./rx-document.md#update).
-To run the operators, RxDB uses the [mingo library](https://github.com/kofrasa/mingo#updating-documents).
-
-A CRDT operator example:
-
-```js
-const myCRDTOperation = {
- // increment the points field by +1
- $inc: {
- points: 1
- },
- // set the modified field to true
- $set: {
- modified: true
- }
-};
-```
-
-### Operators
-
-At the moment, not all possible operators are implemented in [mingo](https://github.com/kofrasa/mingo#updating-documents), if you need additional ones, you should make a pull request there.
-
-The following operators can be used at this point in time:
-- `$min`
-- `$max`
-- `$inc`
-- `$set`
-- `$unset`
-- `$push`
-- `$addToSet`
-- `$pop`
-- `$pullAll`
-- `$rename`
-
-For the exact definition on how each operator behaves, check out the [MongoDB documentation on update operators](https://www.mongodb.com/docs/manual/reference/operator/update/).
-
-## Installation
-
-To use CRDTs with RxDB, you need the following:
-
-- Add the CRDT plugin via `addRxPlugin`.
-- Add a field to your schema that defines where to store the CRDT operations via `getCRDTSchemaPart()`
-- Set the `crdt` options in your schema.
-- Do **NOT** set a custom conflict handler, the plugin will use its own one.
-
-```ts
-// import the relevant parts from the CRDT plugin
-import {
- getCRDTSchemaPart,
- RxDBcrdtPlugin
-} from 'rxdb/plugins/crdt';
-
-// add the CRDT plugin to RxDB
-import { addRxPlugin } from 'rxdb';
-addRxPlugin(RxDBcrdtPlugin);
-
-// create a database
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-const myDatabase = await createRxDatabase({
- name: 'heroesdb',
- storage: getRxStorageLocalstorage()
-});
-
-// create a schema with the CRDT options
-const mySchema = {
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- points: {
- type: 'number',
- maximum: 100,
- minimum: 0
- },
- crdts: getCRDTSchemaPart() // use this field to store the CRDT operations
- },
- required: ['id', 'points'],
- crdt: { // CRDT options
- field: 'crdts'
- }
-}
-
-// add a collection
-await db.addCollections({
- users: {
- schema: mySchema
- }
-});
-
-// insert a document
-const myDocument = await db.users.insert({id: 'alice', points: 0});
-
-// run a CRDT operation that increments the 'points' by one
-await myDocument.updateCRDT({
- ifMatch: {
- $inc: {
- points: 1
- }
- }
-});
-```
-
-## Conditional CRDT operations
-
-By default, all CRDTs operations will be run to build the current document state. But in many cases, more granular operations are required to better reflect the desired business logic. For these cases, conditional CRDTs can be used.
-
-For example if you have a field `points` with a `maximum` of `100`, you might want to only run the `$inc` operation, if the `points` value is less than `100`.
-In an conditional CRDT, you can specify a `selector` and the operation sets `ifMatch` and `ifNotMatch`. At each time the CRDT is applied to the document state, first the selector will run and evaluate which operations path must be used.
-
-```ts
-await myDocument.updateCRDT({
- // only if the selector matches, the ifMatch operation will run
- selector: {
- age: {
- $lt: 100
- }
- },
- // an operation that runs if the selector matches
- ifMatch: {
- $inc: {
- points: 1
- }
- },
- // if the selector does NOT match, you could run a different operation instead
- ifNotMatch: {
- // ...
- }
-});
-```
-
-## Running multiples operations at once
-
-By default, one CRDT operation is applied to the document in a single database write.
-To represent more complex logic chains, it might make sense to use multiple CRDTs and write them at once inside of a single atomic document write.
-
-For these cases, the `updateCRDT()` method allows to pass an array of operations.
-
-```ts
-await myDocument.updateCRDT([
- {
- selector: { /** ... **/ },
- ifMatch: { /** ... **/ }
- },
- {
- selector: { /** ... **/ },
- ifMatch: { /** ... **/ }
- },
- {
- selector: { /** ... **/ },
- ifMatch: { /** ... **/ }
- },
- {
- selector: { /** ... **/ },
- ifMatch: { /** ... **/ }
- }
-]);
-```
-
-## CRDTs on inserts
-
-When CRDTs are enabled with the plugin, all insert operations are automatically mapped as CRDT operation with the `$set` operator.
-
-```ts
-// Calling RxCollection.insert()
-await myRxCollection.insert({
- id: 'foo'
- points: 1
-});
-// is exactly equal to calling insertCRDT()
-await myRxCollection.insertCRDT({
- ifMatch: {
- $set: {
- id: 'foo'
- points: 1
- }
- }
-});
-```
-
-When the same document is inserted in multiple client instances and then replicated, a conflict will emerge and the insert-CRDTs will overwrite each other in a deterministic order.
-You can use `insertCRDT()` to make conditional insert operations with any logic. To check for the previous existence of a document, use the `$exists` query operation on the primary key of the document.
-
-```ts
-await myRxCollection.insertCRDT({
- selector: {
- // only run if the document did not exist before.
- id: { $exists: false }
- },
- ifMatch: {
- // if the document did not exist, insert it
- $set: {
- id: 'foo'
- points: 1
- }
- },
- ifNotMatch: {
- // if document existed already, increment the points by +1
- $inc: {
- points: 1
- }
- }
-});
-```
-
-## Deleting documents
-
-You can delete a document with a CRDT operation by setting `_deleted` to true. Calling `RxDocument.remove()` will do exactly the same when CRDTs are activated.
-
-```ts
-await doc.updateCRDT({
- ifMatch: {
- $set: {
- _deleted: true
- }
- }
-});
-
-// OR
-await doc.remove();
-```
-
-## CRDTs with replication
-
-CRDT operations are stored inside of a special field besides your 'normal' document fields.
-When replicating document data with the [RxDB replication](./replication.md) or the [CouchDB replication](./replication-couchdb.md) or even any custom replication, the CRDT operations must be replicated together with the document data as if they would be 'normal' a document property.
-
-When any instances makes a write to the document, it is required to update the CRDT operations accordingly. For example if your custom backend updates a document, it must also do that by adding a CRDT operation. In [dev-mode](./dev-mode.md) RxDB will refuse to store any document data where the document properties do not match the result of the CRDT operations.
-
-## Why not automerge.js or yjs?
-
-There are already CRDT libraries out there that have been considered to be used with RxDB. The biggest ones are [automerge](https://github.com/automerge/automerge) and [yjs](https://github.com/yjs/yjs). The decision was made to not use these but instead go for a more NoSQL way of designing the CRDT format because:
-
-- Users do not have to learn a new syntax but instead can use the NoSQL query operations which they already know to manipulate the JSON data of a document.
-- RxDB is often used to [replicate](./replication.md) data with any custom backend on an already existing infrastructure. Using NoSQL operators instead of binary data in CRDTs, makes it easy to implement the exact same logic on these backends so that the backend can also do document writes and still be compliant to the RxDB CRDT plugin.
-
-So instead of using YJS or Automerge with a database, you can use RxDB with the CRDT plugin to have a more database specific CRDT approach. This gives you additional features for free such as [schema validation](./schema-validation.md) or [data migration](./migration-schema.md).
-
-## When to not use CRDTs
-
-CRDT can only be use when your business logic allows to represent document changes via static json operators.
-If you can have cases where user interaction is required to correctly merge conflicting document states, you cannot use CRDTs for that.
-
-Also when CRDTs are used, it is no longer allowed to do non-CRDT writes to the document properties.
-
-## CRDT Alternative
-
-While the CRDT plugin can automatically merge concurrent document updates, it is not the only way to resolve conflicts in RxDB.
-An alternative approach to CRDT is to use RxDB's built-in [conflict handling system](./transactions-conflicts-revisions.md).
-
-> Why use conflict handlers instead of CRDT?
-
-Conflict handlers offer a **simpler and more flexible** way to manage data conflicts. Instead of encoding changes as CRDT operations, you define how RxDB should decide which document version "wins" with plain JavaScript code. This approach is easier to reason about because it works directly with your domain logic. For example, you can compare timestamps, prioritize certain fields, or even involve user interaction to resolve conflicts.
-
-Conflict handlers are:
-
-* **Easier to understand**: you work with plain document states instead of CRDT operations.
-* **Fully customizable**: you can define any merge strategy, from simple last-write-wins to complex rule-based logic.
-* **Compatible with all data types**: unlike CRDTs, which are best suited for numeric or set-based updates.
-* **Transparent**: you always know which state is being written and why.
-
-### Downsides of CRDTs
-
-CRDTs are powerful for automatic conflict-free merging, but they also come with trade-offs:
-
-* **Higher conceptual complexity**: CRDTs require understanding of operation semantics, version vectors, and merge determinism.
-* **Limited flexibility**: you can only express changes that fit the supported JSON-style update operators.
-* **Difficult debugging**: when merges don't behave as expected, it can be hard to trace the sequence of CRDT operations that led to a state.
-* **Overhead for simple cases**: if your data rarely conflicts or needs human oversight, using CRDTs can add unnecessary complexity.
-
-
-### When to choose conflict handlers
-
-Use conflict handlers as CRDT alternative if:
-* You want full control over merge logic.
-* Your data model includes contextual or user-specific decisions.
-* You prefer a straightforward, rule-based resolution system over automatic merges.
-
-Use CRDTs if:
-* Your app performs frequent offline writes that can be merged deterministically.
-* Your data can be represented as additive, numeric, or array-based updates.
-* You want minimal manual intervention during replication.
-
-Both methods are first-class citizens in RxDB. CRDTs focus on **automatic, deterministic merging**, while conflict handlers emphasize **clarity, flexibility, and control**.
-
-### Example: merging different fields with conflict handlers instead of CRDT
-
-For example, imagine two users edit different fields of the same document at the same time. One updates a `name`, the other updates a `score`. A custom conflict handler can merge both changes so no data is lost:
-
-```ts
-const mergeFieldsHandler = {
- isEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b),
- resolve: (input) => {
- return {
- ...input.realMasterState,
- name: input.newDocumentState.name ?? input.realMasterState.name,
- score: Math.max(input.newDocumentState.score, input.realMasterState.score)
- };
- }
-};
-```
-
-In this example, if the two versions change different properties, the final merged document includes both updates. This kind of logic is often easier to reason about than designing equivalent CRDT operations.
-
-
diff --git a/docs/data-migration.html b/docs/data-migration.html
deleted file mode 100644
index fbf852635f3..00000000000
--- a/docs/data-migration.html
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-Data Migration | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Thank you for submitting the form. You will directly get a confirmation email. Please check your spam folder!. In the next 24 hours you will get a full answer via email.
The dev-mode plugin adds many checks and validations to RxDB.
-This ensures that you use the RxDB API properly and so the dev-mode plugin should always be used when
-using RxDB in development mode.
-
-
Adds readable error messages.
-
Ensures that readonly JavaScript objects are not accidentally mutated.
-
Adds validation check for validity of schemas, queries, ORM methods and document fields.
-
-
Notice that the dev-mode plugin does not perform schema checks against the data see schema validation for that.
-
-
-
-
warning
The dev-mode plugin will increase your build size and decrease the performance. It must always be used in development. You should never use it in production.
module.exports = {
- entry: './src/index.ts',
- /* ... */
- plugins: [
- // set a global variable that can be accessed during runtime
- new webpack.DefinePlugin({ MODE: JSON.stringify("production") })
- ]
- /* ... */
-};
When the dev-mode is enabled, it will print a console.warn() message to the console so that you do not accidentally use the dev-mode in production. To disable this warning you can call the disableWarnings() function.
-
import { disableWarnings } from 'rxdb/plugins/dev-mode';
-disableWarnings();
When used in localhost and in the browser, the dev-mode plugin can add a tracking iframe to the DOM. This is used to track the effectiveness of marketing efforts of RxDB.
-If you have premium access and want to disable this iframe, you can call setPremiumFlag() before creating the database.
-
import { setPremiumFlag } from 'rxdb-premium/plugins/shared';
-setPremiumFlag();
-
-
\ No newline at end of file
diff --git a/docs/dev-mode.md b/docs/dev-mode.md
deleted file mode 100644
index 901a12333e6..00000000000
--- a/docs/dev-mode.md
+++ /dev/null
@@ -1,116 +0,0 @@
-# Development Mode
-
-> Enable checks & validations with RxDB Dev Mode. Ensure proper API use, readable errors, and schema validation during development. Avoid in production.
-
-import {Steps} from '@site/src/components/steps';
-
-# Dev Mode
-
-The dev-mode plugin adds many checks and validations to RxDB.
-This ensures that you use the RxDB API properly and so the dev-mode plugin should always be used when
-using RxDB in development mode.
-
-- Adds readable error messages.
-- Ensures that `readonly` JavaScript objects are not accidentally mutated.
-- Adds validation check for validity of schemas, queries, [ORM](./orm.md) methods and document fields.
- - Notice that the `dev-mode` plugin does not perform schema checks against the data see [schema validation](./schema-validation.md) for that.
-
-:::warning
-The dev-mode plugin will increase your build size and decrease the performance. It must **always** be used in development. You should **never** use it in production.
-:::
-
-
-
-### Import the dev-mode Plugin
-```javascript
-import { RxDBDevModePlugin } from 'rxdb/plugins/dev-mode';
-import { addRxPlugin } from 'rxdb/plugins/core';
-```
-
-## Add the Plugin to RxDB
-
-```javascript
-addRxPlugin(RxDBDevModePlugin);
-```
-
-
-## Usage with Node.js
-
-```ts
-async function createDb() {
- if (process.env.NODE_ENV !== "production") {
- await import('rxdb/plugins/dev-mode').then(
- module => addRxPlugin(module.RxDBDevModePlugin)
- );
- }
- const db = createRxDatabase( /* ... */ );
-}
-```
-
-## Usage with Angular
-
-```ts
-import { isDevMode } from '@angular/core';
-
-async function createDb() {
- if (isDevMode()){
- await import('rxdb/plugins/dev-mode').then(
- module => addRxPlugin(module.RxDBDevModePlugin)
- );
- }
-
- const db = createRxDatabase( /* ... */ );
- // ...
-}
-```
-
-## Usage with webpack
-
-In the `webpack.config.js`:
-
-```ts
-module.exports = {
- entry: './src/index.ts',
- /* ... */
- plugins: [
- // set a global variable that can be accessed during runtime
- new webpack.DefinePlugin({ MODE: JSON.stringify("production") })
- ]
- /* ... */
-};
-```
-
-In your source code:
-
-```ts
-declare var MODE: 'production' | 'development';
-
-async function createDb() {
- if (MODE === 'development') {
- await import('rxdb/plugins/dev-mode').then(
- module => addRxPlugin(module.RxDBDevModePlugin)
- );
- }
- const db = createRxDatabase( /* ... */ );
- // ...
-}
-```
-
-## Disable the dev-mode warning
-
-When the dev-mode is enabled, it will print a `console.warn()` message to the console so that you do not accidentally use the dev-mode in production. To disable this warning you can call the `disableWarnings()` function.
-
-```ts
-import { disableWarnings } from 'rxdb/plugins/dev-mode';
-disableWarnings();
-```
-
-## Disable the tracking iframe
-
-When used in localhost and in the browser, the dev-mode plugin can add a tracking iframe to the DOM. This is used to track the effectiveness of marketing efforts of RxDB.
-If you have [premium access](/premium/) and want to disable this iframe, you can call `setPremiumFlag()` before creating the database.
-
-```js
-import { setPremiumFlag } from 'rxdb-premium/plugins/shared';
-setPremiumFlag();
-```
diff --git a/docs/downsides-of-offline-first.html b/docs/downsides-of-offline-first.html
deleted file mode 100644
index dea455e0d0f..00000000000
--- a/docs/downsides-of-offline-first.html
+++ /dev/null
@@ -1,133 +0,0 @@
-
-
-
-
-
-Downsides of Local First / Offline First | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
So you have read all these things about how the local-first (aka offline-first) paradigm makes it easy to create realtime web applications that even work when the user has no internet connection.
-But there is no free lunch. The offline first paradigm is not the perfect approach for all kinds of apps.
-
You fully understood a technology when you know when not to use it
In the following I will point out the limitations you need to know before you decide to use RxDB or even before you decide to create an offline first application.
Making data available offline means it must be loaded from the server and then stored at the clients device.
-You need to load the full dataset on the first pageload and on every ongoing load you need to download the new changes to that set.
-While in theory you could download in infinite amount of data, in practice you have a limit how long the user can wait before having an up-to-date state.
-You want to display chat messages like Whatsapp? No problem. Syncing all the messages a user could write, can be done with a few HTTP requests.
-Want to make a tool that displays server logs? Good luck downloading terabytes of data to the client just to search for a single string. This will not work.
-
Besides the network usage, there is another limit for the size of your data.
-In browsers you have some options for storage: Cookies, Localstorage, WebSQL and IndexedDB.
-
Because Cookies and Localstorage is slow and WebSQL is deprecated, you will use IndexedDB.
-The limit of how much data you can store in IndexedDB depends on two factors: Which browser is used and how much disc space is left on the device. You can assume that at least a couple of hundred megabytes are available at least. The maximum is potentially hundreds of gigabytes or more, but the browser implementations vary. Chrome allows the browser to use up to 60% of the total disc space per origin. Firefox allows up to 50%. But on safari you can only store up to 1GB and the browser will prompt the user on each additional 200MB increment.
-
The problem is, that you have no chance to really predict how much data can be stored. So you have to make assumptions that are hopefully true for all of your users. Also, you have no way to increase that space like you would add another hard drive to your backend server. Once your clients reach the limit, you likely have to rewrite big parts of your applications.
-
UPDATE (2023): Newer versions of browsers can store way more data, for example firefox stores up to 10% of the total disk size. For an overview about how much can be stored, read this guide
When data is stored inside IndexedDB or one of the other storage APIs, it cannot be trusted to stay there forever.
-Apple for example deletes the data when the website was not used in the last 7 days. The other browsers also have logic to clean up the stored data, and in the end the user itself could be the one that deletes the browsers local data.
-
The most common way to handle this, is to replicate everything from the backend to the client again.
-Of course, this does not work for state that is not stored at the backend. So if you assume you can store the users private data inside the browser in a secure way, you are wrong.
Imagine two of your users modify the same JSON document, while both are offline. After they go online again, their clients replicate the modified document to the server. Now you have two conflicting versions of the same document, and you need a way to determine how the correct new version of that document should look like. This process is called conflict resolution.
-
-
-
-
The default in many offline first databases is a deterministic conflict resolution strategy. Both conflicting versions of the document are kept in the storage and when you query for the document, a winner is determined by comparing the hashes of the document and only the winning document is returned. Because the comparison is deterministic, all clients and servers will always pick the same winner. This kind of resolution only works when it is not that important that one of the document changes gets dropped. Because conflicts are rare, this might be a viable solution for some use cases.
-
-
-
A better resolution can be applied by listening to the changestream of the database. The changestream emits an event each time a write happens to the database. The event contains information about the written document and also a flag if there is a conflicting version. For each event with a conflict, you fetch all versions for that document and create a new document that contains the winning state. With that you can implement pretty complex conflict resolution strategies, but you have to manually code it for each collection of documents.
-
-
-
Instead of the solving conflict once at every client, it can be made a bit easier by solely relying on the backend. This can be done when all of your clients replicate with the same single backend server. With RxDB's Graphql Replication each client side change is sent to the server where conflicts can be resolved and the winning document can be sent back to the clients.
-
-
-
Sometimes there is no way to solve a conflict with code. If your users edit text based documents or images, often only the users themselves can decide how the winning revision has to look. For these cases, you have to implement complex UI parts where the users can inspect the conflict and manage its resolution.
-
-
-
You do not have to handle conflicts if they cannot happen in the first place. You can achieve that by designing a write only database where existing documents cannot be touched. Instead of storing the current state in a single document, you store all the events that lead to the current state. Sometimes called the "everything is a delta" strategy, others would call it Event Sourcing. Like an accountant that does not need an eraser, you append all changes and afterwards aggregate the current state at the client.
-
-
-
// create one new document for each change to the users balance
-{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
-
-
There is this thing called conflict-free replicated data type, short CRDT. Using a CRDT library like automerge will magically solve all of your conflict problems. Until you use it in production where you observe that implementing CRDTs has basically the same complexity as implementing conflict resolution strategies.
So you replicate stuff between the clients and your backend. Each change on one side directly changes the state of the other sides in realtime. But this "realtime" is not the same as in realtime computing. In the offline first world, the word realtime was introduced by firebase and is more meant as a marketing slogan than a technical description.
-There is an internet between your backend and your clients and everything you do on one machine takes at least once the latency until it can affect anything on the other machines. You have to keep this in mind when you develop anything where the timing is important, like a multiplayer game or a stock trading app.
-
Even when you run a query against the local database, there is no "real" realtime.
-Client side databases run on JavaScript and JavaScript runs on a single CPU that might be partially blocked because the user is running some background processes. So you can never guarantee a response deadline which violates the time constraint of realtime computing.
An offline first app does not have a single source of truth. There is a source on the backend, one on the own client, and also each other client has its own definition of truth. At the moment your user starts the app, the local state is hopefully already replicated with the backend and all other clients. But this does not have to be true, the states can have converged and you have to plan for that.
-The user could update a document based on wrong assumptions because it was not fully replicated at that point in time because the user is offline. A good way to handle this problem is to show the replication state in the UI and tell the user when the replication is running, stopped, paused or finished.
-
And some data is just too important to be "eventual consistent". Create a wire transfer in your online banking app while you are offline. You keep the smartphone laying at your night desk and when you use again in the next morning, it goes online and replicates the transaction. No thank you, do not use offline first for these kinds of things, or at least you have to display the replication state of each document in the UI.
Every offline first app that goes beyond a prototype, does likely not have the same global state for all of its users. Each user has a different set of documents that are allowed to be replicated or seen by the user. So you need some kind of authentication and permission handling to divide the documents.
-The easy way is to just create one database for each user on the backend and only allow to replicate that one. Creating that many databases is not really a problem with for example CouchDB, and it makes permission handling easy.
-But as soon as you want to query all of your data in the backend, it will bite back. Your data is not at a single place, it is distributed between all of the user specific databases. This becomes even more complex as soon as you store information together with the documents that is not allowed to be seen by outsiders. You not only have to decide which documents to replicate, but also which fields of them.
-
So what you really want is a single datastore in the backend and then replicate only the allowed document parts to each of the users.
-This always requires you to implement your custom replication endpoint like what you do with RxDBs GraphQL Replication.
While developing your app, sooner or later you want to change the data layout. You want to add some new fields to documents or change the format of them. So you have to update the database schema and also migrate the stored documents.
-With 'normal' applications, this is already hard enough and often dangerous. You wait until midnight, stop the webserver, make a database backup, deploy the new schema and then you hope that nothing goes wrong while it updates that many documents.
-
With offline first applications, it is even more fun. You do not only have to migrate your local backend database, you also have to provide a migration strategy for all of these client databases out there. And you also cannot migrate everything at the same time. The clients can only migrate when the new code was updated from the appstore or the user visited your website again. This could be today or in a few weeks.
When you create a web based offline first app, you cannot store data directly on the users filesystem. In fact there are many layers between your JavaScript code and the filesystem of the operation system. Let's say you insert a document in RxDB:
-
-
You call the RxDB API to validate and store the data
-
RxDB calls the underlying RxStorage, for example PouchDB.
-
Pouchdb calls its underlying storage adapter
-
The storage adapter calls IndexedDB
-
The browser runs its internal handling of the IndexedDB API
-
In most browsers IndexedDB is implemented on top of SQLite
-
SQLite calls the OS to store the data in the filesystem
-
-
All these layers are abstractions. They are not build for exactly that one use case, so you lose some performance to tunnel the data through the layer itself, and you also lose some performance because the abstraction does not exactly provide the functions that are needed by the layer above and it will overfetch data.
-
You will not find a benchmark comparison between how many transactions per second you can run on the browser compared to a server based database. Because it makes no sense to compare them. Browsers are slower, JavaScript is slower.
-
-
Is it fast enough?
-
-
What you really care about is "Is it fast enough?". For most use cases, the answer is yes. Offline first apps are UI based and you do not need to process a million transactions per second, because your user will not click the save button that often. "Fast enough" means that the data is processed in under 16 milliseconds so that you can render the updated UI in the next frame. This is of course not true for all use cases, so you better think about the performance limit before starting with the implementation.
You have a PostgreSQL database and run a query over 1000ths of rows, which takes 200 milliseconds. Works great, so you now want to do something similar at the client device in your offline first app. How long does it take? You cannot know because people have different devices, and even equal devices have different things running in the background that slow the CPUs. So you cannot predict performance and as described above, you cannot even predict the storage limit. So if your app does heavy data analytics, you might better run everything on the backend and just send the results to the client.
I started creating RxDB many years ago and while still maintaining it, I often worked with all these other offline first databases out there. RxDB and all of these other ones, are based on some kind of document databases similar to NoSQL. Often people want to have a relational database like the SQL one they use at the backend.
-
So why are there no real relations in offline first databases? I could answer with these arguments like how JavaScript works better with document based data, how performance is better when having no joins or even how NoSQL queries are more composable. But the truth is, everything is NoSQL because it makes replication easy. An SQL query that mutates data in different tables based on some selects and joins, cannot be partially replicated without breaking the client. You have foreign keys that point to other rows and if these rows are not replicated yet, you have a problem. To implement a robust Sync Engine for relational data, you need some stuff like a reliable atomic clock and you have to block queries over multiple tables while a transaction replicated. Watch this guy implementing offline first replication on top of SQLite or read this discussion about implementing offline first in supabase.
-
So creating replication for an SQL offline first database is way more work than just adding some network protocols on top of PostgreSQL. It might not even be possible for clients that have no reliable clock.
-
-
\ No newline at end of file
diff --git a/docs/downsides-of-offline-first.md b/docs/downsides-of-offline-first.md
deleted file mode 100644
index 80fe94f1518..00000000000
--- a/docs/downsides-of-offline-first.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# Downsides of Local First / Offline First
-
-> Discover the hidden pitfalls of local-first apps. Learn about storage limits, conflicts, and real-time illusions before building your offline solution.
-
-import {QuoteBlock} from '@site/src/components/quoteblock';
-
-# Downsides of Local First / Offline First
-
-So you have read [all these things](./offline-first.md) about how the [local-first](./articles/local-first-future.md) (aka offline-first) paradigm makes it easy to create realtime web applications that even work when the user has no internet connection.
-But there is no free lunch. The offline first paradigm is not the perfect approach for all kinds of apps.
-
-You fully understood a technology when you know when not to use it
-
-In the following I will point out the limitations you need to know before you decide to use [RxDB](https://github.com/pubkey/rxdb) or even before you decide to create an offline first application.
-
-## It only works with small datasets
-
-Making data available offline means it must be loaded from the server and then stored at the clients device.
-You need to load the full dataset on the first pageload and on every ongoing load you need to download the new changes to that set.
-While in theory you could download in infinite amount of data, in practice you have a limit how long the user can wait before having an up-to-date state.
-You want to display chat messages like Whatsapp? No problem. Syncing all the messages a user could write, can be done with a few HTTP requests.
-Want to make a tool that displays server logs? Good luck downloading terabytes of data to the client just to search for a single string. This will not work.
-
-Besides the network usage, there is another limit for the size of your data.
-In browsers you have some options for storage: Cookies, [Localstorage](./articles/localstorage.md), [WebSQL](./articles/localstorage-indexeddb-cookies-opfs-sqlite-wasm.md#what-was-websql) and [IndexedDB](./rx-storage-indexeddb.md).
-
-Because Cookies and [Localstorage](./articles/localstorage.md) is slow and WebSQL is deprecated, you will use IndexedDB.
-The [limit of how much data you can store in IndexedDB](./articles/indexeddb-max-storage-limit.md) depends on two factors: Which browser is used and how much disc space is left on the device. You can assume that at least a couple of [hundred megabytes](https://web.dev/storage-for-the-web/) are available at least. The maximum is potentially hundreds of gigabytes or more, but the browser implementations vary. Chrome allows the browser to use up to 60% of the total disc space per origin. Firefox allows up to 50%. But on safari you can only store up to 1GB and the browser will prompt the user on each additional 200MB increment.
-
-The problem is, that you have no chance to really predict how much data can be stored. So you have to make assumptions that are hopefully true for all of your users. Also, you have no way to increase that space like you would add another hard drive to your backend server. Once your clients reach the limit, you likely have to rewrite big parts of your applications.
-
-UPDATE (2023): Newer versions of browsers can store way more data, for example firefox stores up to 10% of the total disk size. For an overview about how much can be stored, read [this guide](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria)
-
-## Browser storage is not really persistent
-
-When data is stored inside IndexedDB or one of the other storage APIs, it cannot be trusted to stay there forever.
-Apple for example deletes the data when the website was not used in the [last 7 days](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/). The other browsers also have logic to clean up the stored data, and in the end the user itself could be the one that deletes the browsers local data.
-
-The most common way to handle this, is to replicate everything from the backend to the client again.
-Of course, this does not work for state that is not stored at the backend. So if you assume you can store the users private data inside the browser in a secure way, you are [wrong](https://medium.com/universal-ethereum/out-of-gas-were-shutting-down-unilogin-3b544838df1a#4f60).
-
-
-
-## There can be conflicts
-
-Imagine two of your users modify the same JSON document, while both are offline. After they go online again, their clients replicate the modified document to the server. Now you have two conflicting versions of the same document, and you need a way to determine how the correct new version of that document should look like. This process is called **conflict resolution**.
-
-
-
- 1. The default in [many](https://docs.couchdb.org/en/stable/replication/conflicts.html) offline first databases is a deterministic conflict resolution strategy. Both conflicting versions of the document are kept in the storage and when you query for the document, a winner is determined by comparing the hashes of the document and only the winning document is returned. Because the comparison is deterministic, all clients and servers will always pick the same winner. This kind of resolution only works when it is not that important that one of the document changes gets dropped. Because conflicts are rare, this might be a viable solution for some use cases.
-
- 2. A better resolution can be applied by listening to the changestream of the database. The changestream emits an event each time a write happens to the database. The event contains information about the written document and also a flag if there is a conflicting version. For each event with a conflict, you fetch all versions for that document and create a new document that contains the winning state. With that you can implement pretty complex conflict resolution strategies, but you have to manually code it for each collection of documents.
-
- 3. Instead of the solving conflict once at every client, it can be made a bit easier by solely relying on the backend. This can be done when all of your clients replicate with the same single backend server. With [RxDB's Graphql Replication](./replication-graphql.md) each client side change is sent to the server where conflicts can be resolved and the winning document can be sent back to the clients.
-
- 4. Sometimes there is no way to solve a conflict with code. If your users edit text based documents or images, often only the users themselves can decide how the winning revision has to look. For these cases, you have to implement complex UI parts where the users can inspect the conflict and manage its resolution.
-
- 5. You do not have to handle conflicts if they cannot happen in the first place. You can achieve that by designing a write only database where existing documents cannot be touched. Instead of storing the current state in a single document, you store all the events that lead to the current state. Sometimes called the ["everything is a delta"](https://pouchdb.com/guides/conflicts.html#accountants-dont-use-erasers) strategy, others would call it [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html). Like an accountant that does not need an eraser, you append all changes and afterwards aggregate the current state at the client.
- ```ts
- // create one new document for each change to the users balance
- {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
- ```
-
- 6. There is this thing called **conflict-free replicated data type**, short **CRDT**. Using a CRDT library like [automerge](https://github.com/automerge/automerge) will magically solve all of your conflict problems. Until you use it in production where you observe that implementing CRDTs has basically the same complexity as implementing conflict resolution strategies.
-
-## Realtime is a lie
-
-So you replicate stuff between the clients and your backend. Each change on one side directly changes the state of the other sides in **realtime**. But this "realtime" is not the same as in [realtime computing](https://en.wikipedia.org/wiki/Real-time_computing). In the offline first world, the word realtime was introduced by firebase and is more meant as a marketing slogan than a technical description.
-There is an internet between your backend and your clients and everything you do on one machine takes at least once the latency until it can affect anything on the other machines. You have to keep this in mind when you develop anything where the timing is important, like a multiplayer game or a stock trading app.
-
-Even when you run a query against the local database, there is no "real" realtime.
-Client side databases run on JavaScript and JavaScript runs on a single CPU that might be partially blocked because the user is running some background processes. So you can never guarantee a response deadline which violates the time constraint of realtime computing.
-
-
-
-## Eventual consistency
-
-An offline first app does not have a single source of truth. There is a source on the backend, one on the own client, and also each other client has its own definition of truth. At the moment your user starts the app, the local state is hopefully already replicated with the backend and all other clients. But this does not have to be true, the states can have converged and you have to plan for that.
-The user could update a document based on wrong assumptions because it was not fully replicated at that point in time because the user is offline. A good way to handle this problem is to show the replication state in the UI and tell the user when the replication is running, stopped, paused or finished.
-
-And some data is just too important to be "eventual consistent". Create a wire transfer in your online banking app while you are offline. You keep the smartphone laying at your night desk and when you use again in the next morning, it goes online and replicates the transaction. No thank you, do not use offline first for these kinds of things, or at least you have to display the replication state of each document in the UI.
-
-
-
-## Permissions and authentication
-
-Every offline first app that goes beyond a prototype, does likely not have the same global state for all of its users. Each user has a different set of documents that are allowed to be replicated or seen by the user. So you need some kind of authentication and permission handling to divide the documents.
-The easy way is to just create one database for each user on the backend and only allow to replicate that one. Creating that many databases is not really a problem with for example CouchDB, and it makes permission handling easy.
-But as soon as you want to query all of your data in the backend, it will bite back. Your data is not at a single place, it is distributed between all of the user specific databases. This becomes even more complex as soon as you store information together with the documents that is not allowed to be seen by outsiders. You not only have to decide which documents to replicate, but also which fields of them.
-
-So what you really want is a single datastore in the backend and then replicate only the allowed document parts to each of the users.
-This always requires you to implement your custom replication endpoint like what you do with RxDBs [GraphQL Replication](./replication-graphql.md).
-
-## You have to migrate the client database
-
-While developing your app, sooner or later you want to change the data layout. You want to add some new fields to documents or change the format of them. So you have to update the database schema and also migrate the stored documents.
-With 'normal' applications, this is already hard enough and often dangerous. You wait until midnight, stop the webserver, make a database backup, deploy the new schema and then you hope that nothing goes wrong while it updates that many documents.
-
-With offline first applications, it is even more fun. You do not only have to migrate your local backend database, you also have to provide a [migration strategy](./migration-schema.md) for all of these client databases out there. And you also cannot migrate everything at the same time. The clients can only migrate when the new code was updated from the appstore or the user visited your website again. This could be today or in a few weeks.
-
-## Performance is not native
-
-When you create a web based offline first app, you cannot store data directly on the users filesystem. In fact there are many layers between your JavaScript code and the filesystem of the operation system. Let's say you insert a document in [RxDB](https://github.com/pubkey/rxdb):
- - You call the RxDB API to validate and store the data
- - RxDB calls the underlying RxStorage, for example PouchDB.
- - Pouchdb calls its underlying storage adapter
- - The storage adapter calls IndexedDB
- - The browser runs its internal handling of the IndexedDB API
- - In most browsers IndexedDB is implemented on [top of SQLite](https://hackaday.com/2021/08/24/sqlite-on-the-web-absurd-sql/)
- - SQLite calls the OS to store the data in the filesystem
-
-All these layers are abstractions. They are not build for exactly that one use case, so you lose some performance to tunnel the data through the layer itself, and you also lose some performance because the abstraction does not exactly provide the functions that are needed by the layer above and it will overfetch data.
-
-You will not find a benchmark comparison between how many transactions per second you can run on the browser compared to a server based database. Because it makes no sense to compare them. Browsers are slower, JavaScript is slower.
-
-> **Is it fast enough?**
-
-What you really care about is "Is it fast enough?". For most use cases, the answer is `yes`. Offline first apps are UI based and you do not need to process a million transactions per second, because your user will not click the save button that often. "Fast enough" means that the data is processed in under 16 milliseconds so that you can render the updated UI in the next frame. This is of course not true for all use cases, so you better think about the performance limit before starting with the implementation.
-
-## Nothing is predictable
-
-You have a PostgreSQL database and run a query over 1000ths of rows, which takes 200 milliseconds. Works great, so you now want to do something similar at the client device in your offline first app. How long does it take? You cannot know because people have different devices, and even equal devices have different things running in the background that slow the CPUs. So you cannot predict performance and as described above, you cannot even predict the storage limit. So if your app does heavy data analytics, you might better run everything on the backend and just send the results to the client.
-
-## There is no relational data
-
-I started creating [RxDB](https://github.com/pubkey/rxdb) many years ago and while still maintaining it, I often worked with all these other offline first databases out there. RxDB and all of these other ones, are based on some kind of document databases similar to NoSQL. Often people want to have a relational database like the SQL one they use at the backend.
-
-So why are there no real relations in offline first databases? I could answer with these arguments like how JavaScript works better with document based data, how performance is better when having no joins or even how NoSQL queries are more composable. But the truth is, everything is NoSQL because it makes replication easy. An SQL query that mutates data in different tables based on some selects and joins, cannot be partially replicated without breaking the client. You have foreign keys that point to other rows and if these rows are not replicated yet, you have a problem. To implement a robust [Sync Engine](./replication.md) for relational data, you need some stuff like a [reliable atomic clock](https://www.theverge.com/2012/11/26/3692392/google-spanner-atomic-clocks-GPS) and you have to block queries over multiple tables while a transaction replicated. [Watch this guy](https://youtu.be/iEFcmfmdh2w?t=607) implementing offline first replication on top of SQLite or read this [discussion](https://github.com/supabase/supabase/discussions/357) about implementing [offline first in supabase](./replication-supabase.md).
-
-So creating replication for an SQL offline first database is way more work than just adding some network protocols on top of PostgreSQL. It might not even be possible for clients that have no reliable clock.
diff --git a/docs/electron-database.html b/docs/electron-database.html
deleted file mode 100644
index 658ccbe1724..00000000000
--- a/docs/electron-database.html
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
-
-
-Electron Database - Storage adapters for SQLite, Filesystem and In-Memory | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Electron Database - RxDB with different storage for SQLite, Filesystem and In-Memory
-
Electron (aka Electron.js) is a framework developed by github that is designed to create desktop applications with the Web technology stack consisting of HTML, CSS and JavaScript.
-Because the desktop application runs on the client's device, it is suitable to use a database that can store and query data locally. This allows you to create so-called local first apps that store data locally and even work when the user has no internet connection.
-While there are many options to store data in Electron, for complex realtime apps using RxDB is recommended because it is a database made for UI-based client-side application, not a server-side database.
An Electron runtime can be divided into two parts:
-
-
The "main" process which is a Node.js JavaScript process that runs without a UI in the background.
-
One or multiple "renderer" processes that consist of a Chrome browser engine and runs the user interface. Each renderer process represents one "browser tab".
-
-
This is important to understand because choosing the right database depends on your use case and on which of these JavaScript runtimes you want to keep the data.
Because Electron runs on a desktop computer, you might think that it should be possible to use a common "server" database like MySQL, PostgreSQL or MongoDB. In theory, you could ship the correct database server binaries with your electron application and start a process on the client's device that exposes a port to the database that can be consumed by Electron. In practice, this is not a viable way to go because shipping the correct binaries and opening ports is way to complicated and troublesome. Instead you should use a database that can be bundled and run inside of Electron, either in the main or in the renderer process.
-
Localstorage / IndexedDB / WebSQL as alternatives to SQLite in Electron
-
Because Electron uses a common Chrome web browser in the renderer process, you can access the common Web Storage APIs like Localstorage, IndexedDB and WebSQL. This is easy to set up and storing small sets of data can be achieved in a short span of time.
-
But as soon as your application goes beyond a simple todo-app, there are multiple obstacles that come in your way. One thing is the bad multi-tab support. If you have more than one renderer process, it becomes hard to manage database writes between them. Each browser tab could modify the database state while the others do not know of the changes and keep an outdated UI.
-
Another thing is performance. IndexedDB is slow, mostly because it has to go through layers of browser security and abstractions. Storing and querying a lot of data might become your performance bottleneck. Localstorage and WebSQL are even slower, by the way. Using these Web Storage APIs is generally only recommended when you know for sure that there will be always only one rendering process and performance is not that relevant. The main reason for that is the security- and abstraction layers that write- and read operations have to go through when using the browsers IndexedDB API. So instead of using IndexedDB in Electron in the renderer process, you should use something that runs in the "main" process in Node.js like the Filesystem RxStorage or the In Memory RxStorage.
RxDB is a NoSQL database for JavaScript applications. It has many features that come in handy when RxDB is used with UI based applications like your Electron app. For example, it is able to subscribe to query results of single fields of documents. It has encryption and compression features and most important it has a battle tested Sync Engine that can be used to do a realtime sync with your backend.
-
Because of the flexible storage layer of RxDB, there are many options on how to use it with Electron:
-
-
The memory RxStorage that stores the data inside of the JavaScript memory without persistence
It is recommended to use the SQLite RxStorage because it has the best performance and is the easiest to set up. However it is part of the 👑 Premium Plugins which must be purchased, so to try out RxDB with Electron, you might want to use one of the other options. To start with RxDB, I would recommend using the LocalStorage RxStorage in the renderer processes. Because RxDB is able to broadcast the database state between browser tabs, having multiple renderer processes is not a problem like it would be when you use plain IndexedDB without RxDB.
-In production, you would always run the RxStorage in the main process with the RxStorage Electron IpcRenderer & IpcMain plugins.
-
First, you have to install all dependencies via npm install rxdb rxjs.
-Then you can assemble the RxStorage and create a database with it:
For better performance in the renderer tab, you can later switch to the IndexedDB RxStorage. But in production, it is recommended to use the SQLite RxStorage or the Filesystem RxStorage in the main process so that database operations do not block the rendering of the UI.
-To learn more about using RxDB with Electron, you might want to check out this example project.
SQLite is a SQL based relational database written in the C programming language that was crafted to be embedded inside of applications and stores data locally. Operations are written in the SQL query language similar to the PostgreSQL syntax.
-
Using SQLite in Electron is not possible in the renderer process, only in the main process. To communicate data operations between your main and your renderer processes, you have to use either @electron/remote (not recommended) or the ipcRenderer (recommended). So you start up SQLite in your main process and whenever you want to read or write data, you send the SQL queries to the main process and retrieve the result back as JSON data.
-
To install SQLite, use the SQLite3 package which is a native Node.js module. You also need the @electron/rebuild package to rebuild the SQLite module against the currently installed Electron version.
-
Install them with npm install sqlite3 @electron/rebuild.
-Then you can rebuild SQLite with ./node_modules/.bin/electron-rebuild -f -w sqlite3
-In the JavaScript code of your main process you can now create a database:
-
const sqlite3 = require('sqlite3');
-const db = new sqlite3.Database('/path/to/database/file.db');
-// create a table and insert a row
-db.serialize(() => {
- db.run("CREATE TABLE Users (name, lastName)");
- db.run("INSERT INTO Users VALUES (?, ?)", ['foo', 'bar']);
-});
-
Also you have to set up the ipcRenderer so that message from the renderer process are handled:
In your renderer process, you can now call the ipcHandler and fetch data from SQLite:
-
const rows = await ipcRenderer.invoke('db-query', "SELECT * FROM Users");
-
The downside of SQLite (or SQL in general) is that it is lacking many features that are handful when using a database together with UI based applications. It is not possible to observe queries or document fields and there is no replication method to sync data with a server. This makes SQLite a good solution when you just want to store data on the client or process expensive SQL queries on the server, but it is not suitable for more complex operations like two-way replication, encryption, compression and so on. Also developer helpers like TypeScript type safety are totally out of reach.
-
-
\ No newline at end of file
diff --git a/docs/electron-database.md b/docs/electron-database.md
deleted file mode 100644
index b93643a5d64..00000000000
--- a/docs/electron-database.md
+++ /dev/null
@@ -1,139 +0,0 @@
-# Electron Database - Storage adapters for SQLite, Filesystem and In-Memory
-
-> Harness the database power of SQLite, Filesystem, and in-memory storage in Electron with RxDB. Build fast, offline-first apps that sync in real time.
-
-# Electron Database - RxDB with different storage for SQLite, Filesystem and In-Memory
-
-[Electron](https://www.electronjs.org/) (aka Electron.js) is a framework developed by github that is designed to create desktop applications with the Web technology stack consisting of HTML, CSS and JavaScript.
-Because the desktop application runs on the client's device, it is suitable to use a database that can store and query data locally. This allows you to create so-called [local first](./offline-first.md) apps that store data locally and even work when the user has no internet connection.
-While there are many options to store data in Electron, for complex realtime apps using [RxDB](https://rxdb.info/) is recommended because it is a database made for UI-based client-side application, not a server-side database.
-
-
-
-## Databases for Electron
-
-An Electron runtime can be divided into two parts:
-- The "main" process which is a Node.js JavaScript process that runs without a UI in the background.
-- One or multiple "renderer" processes that consist of a Chrome browser engine and runs the user interface. Each renderer process represents one "browser tab".
-
-This is important to understand because choosing the right database depends on your use case and on which of these JavaScript runtimes you want to keep the data.
-
-### Server Side Databases in Electron.js
-
-Because Electron runs on a desktop computer, you might think that it should be possible to use a common "server" database like MySQL, PostgreSQL or MongoDB. In theory, you could ship the correct database server binaries with your electron application and start a process on the client's device that exposes a port to the database that can be consumed by Electron. In practice, this is not a viable way to go because shipping the correct binaries and opening ports is way to complicated and troublesome. Instead you should use a database that can be bundled and run **inside** of Electron, either in the *main* or in the *renderer* process.
-
-### Localstorage / IndexedDB / WebSQL as alternatives to SQLite in Electron
-
-Because Electron uses a common Chrome web browser in the renderer process, you can access the common Web Storage APIs like [Localstorage](./articles/localstorage.md), IndexedDB and WebSQL. This is easy to set up and storing small sets of data can be achieved in a short span of time.
-
-But as soon as your application goes beyond a simple todo-app, there are multiple obstacles that come in your way. One thing is the bad multi-tab support. If you have more than one *renderer* process, it becomes hard to manage database writes between them. Each *browser tab* could modify the database state while the others do not know of the changes and keep an outdated UI.
-
-Another thing is performance. [IndexedDB is slow](./slow-indexeddb.md), mostly because it has to go through layers of browser security and abstractions. Storing and querying a lot of data might become your performance bottleneck. Localstorage and WebSQL are even slower, by the way. Using these Web Storage APIs is generally only recommended when you know for sure that there will be always only **one rendering process** and performance is not that relevant. The main reason for that is the security- and abstraction layers that write- and read operations have to go through when using the browsers IndexedDB API. So instead of using IndexedDB in Electron in the renderer process, you should use something that runs in the "main" process in Node.js like the [Filesystem RxStorage](./rx-storage-filesystem-node.md) or the [In Memory RxStorage](./rx-storage-memory.md).
-
-### RxDB
-
-
-
-[RxDB](https://rxdb.info/) is a NoSQL database for JavaScript applications. It has many features that come in handy when RxDB is used with UI based applications like your Electron app. For example, it is able to subscribe to query results of single fields of documents. It has encryption and compression features and most important it has a battle tested [Sync Engine](./replication.md) that can be used to do a realtime sync with your backend.
-
-Because of the [flexible storage](https://rxdb.info/rx-storage.html) layer of RxDB, there are many options on how to use it with Electron:
-
-- The [memory RxStorage](./rx-storage-memory.md) that stores the data inside of the JavaScript memory without persistence
-- The [SQLite RxStorage](./rx-storage-sqlite.md)
-- The [IndexedDB RxStorage](./rx-storage-indexeddb.md)
-- The [LocalStorage RxStorage](./rx-storage-localstorage.md)
-- The [Dexie.js RxStorage](./rx-storage-dexie.md)
-- The [Node.js Filesystem](./rx-storage-filesystem-node.md)
-
-It is recommended to use the [SQLite RxStorage](./rx-storage-sqlite.md) because it has the best performance and is the easiest to set up. However it is part of the [👑 Premium Plugins](/premium/) which must be purchased, so to try out RxDB with Electron, you might want to use one of the other options. To start with RxDB, I would recommend using the LocalStorage RxStorage in the renderer processes. Because RxDB is able to broadcast the database state between browser tabs, having multiple renderer processes is not a problem like it would be when you use plain IndexedDB without RxDB.
-In production, you would always run the RxStorage in the main process with the [RxStorage Electron IpcRenderer & IpcMain](./electron.md#rxstorage-electron-ipcrenderer--ipcmain) plugins.
-
-First, you have to install all dependencies via `npm install rxdb rxjs`.
-Then you can assemble the RxStorage and create a database with it:
-
-```ts
-import { createRxDatabase } from 'rxdb';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-// create database
-const db = await createRxDatabase({
- name: 'exampledb',
- storage: getRxStorageLocalstorage()
-});
-
-// create collections
-const collections = await myRxDatabase.addCollections({
- humans: {
- /* ... */
- }
-});
-
-// insert document
-await collections.humans.insert({id: 'foo', name: 'bar'});
-
-// run a query
-const result = await collections.humans.find({
- selector: {
- name: 'bar'
- }
-}).exec();
-
-// observe a query
-await collections.humans.find({
- selector: {
- name: 'bar'
- }
-}).$.subscribe(result => {/* ... */});
-```
-
-For better performance in the renderer tab, you can later switch to the [IndexedDB RxStorage](./rx-storage-indexeddb.md). But in production, it is recommended to use the [SQLite RxStorage](./rx-storage-sqlite.md) or the [Filesystem RxStorage](./rx-storage-filesystem-node.md) in the main process so that database operations do not block the rendering of the UI.
-To learn more about using RxDB with Electron, you might want to check out [this example project](https://github.com/pubkey/rxdb/tree/master/examples/electron).
-
-### SQLite in Electron.js without RxDB
-
-SQLite is a SQL based relational database written in the C programming language that was crafted to be embedded inside of applications and stores data locally. Operations are written in the SQL query language similar to the PostgreSQL syntax.
-
-Using SQLite in Electron is not possible in the *renderer process*, only in the *main process*. To communicate data operations between your main and your renderer processes, you have to use either [@electron/remote](https://github.com/electron/remote) (not recommended) or the [ipcRenderer](https://www.electronjs.org/de/docs/latest/api/ipc-renderer) (recommended). So you start up SQLite in your main process and whenever you want to read or write data, you send the SQL queries to the main process and retrieve the result back as JSON data.
-
-To install SQLite, use the [SQLite3](https://github.com/TryGhost/node-sqlite3) package which is a native Node.js module. You also need the [@electron/rebuild](https://github.com/electron/rebuild) package to rebuild the SQLite module against the currently installed Electron version.
-
-Install them with `npm install sqlite3 @electron/rebuild`.
-Then you can rebuild SQLite with `./node_modules/.bin/electron-rebuild -f -w sqlite3`
-In the JavaScript code of your main process you can now create a database:
-
-```ts
-const sqlite3 = require('sqlite3');
-const db = new sqlite3.Database('/path/to/database/file.db');
-// create a table and insert a row
-db.serialize(() => {
- db.run("CREATE TABLE Users (name, lastName)");
- db.run("INSERT INTO Users VALUES (?, ?)", ['foo', 'bar']);
-});
-```
-
-Also you have to set up the ipcRenderer so that message from the renderer process are handled:
-
-```ts
-ipcMain.handle('db-query', async (event, sqlQuery) => {
- return new Promise(res => {
- db.all(sqlQuery, (err, rows) => {
- res(rows);
- });
- });
-});
-```
-In your renderer process, you can now call the ipcHandler and fetch data from SQLite:
-
-```ts
-const rows = await ipcRenderer.invoke('db-query', "SELECT * FROM Users");
-```
-
-The downside of SQLite (or SQL in general) is that it is lacking many features that are handful when using a database together with **UI based** applications. It is not possible to observe queries or document fields and there is no replication method to sync data with a server. This makes SQLite a good solution when you just want to store data on the client or process expensive SQL queries on the server, but it is not suitable for more complex operations like two-way replication, encryption, compression and so on. Also developer helpers like TypeScript type safety are totally out of reach.
-
-
-
-## Follow up
-
-- Learn how to use RxDB as database in electron with the [Quickstart Tutorial](./quickstart.md).
-- Check out the [RxDB Electron example](https://github.com/pubkey/rxdb/tree/master/examples/electron)
-- There is a followup list of other [client side database alternatives](./alternatives.md) that you can try to use with Electron.
diff --git a/docs/electron.html b/docs/electron.html
deleted file mode 100644
index f5eee3ea223..00000000000
--- a/docs/electron.html
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-Seamless Electron Storage with RxDB | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
To use RxDB in electron, it is recommended to run the RxStorage in the main process and the RxDatabase in the renderer processes. With the rxdb electron plugin you can create a remote RxStorage and consume it from the renderer process.
-
To do this in a convenient way, the RxDB electron plugin provides the helper functions exposeIpcMainRxStorage and getRxStorageIpcRenderer.
-Similar to the Worker RxStorage, these wrap any other RxStorage once in the main process and once in each renderer process. In the renderer you can then use the storage to create a RxDatabase which communicates with the storage of the main process to store and query data.
-
-
\ No newline at end of file
diff --git a/docs/electron.md b/docs/electron.md
deleted file mode 100644
index 689daa63a11..00000000000
--- a/docs/electron.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Seamless Electron Storage with RxDB
-
-> Use the RxDB Electron Plugin to share data between main and renderer processes. Enjoy quick queries, real-time sync, and robust offline support.
-
-# Electron Plugin
-
-## RxStorage Electron IpcRenderer & IpcMain
-
-To use RxDB in [electron](./electron-database.md), it is recommended to run the RxStorage in the main process and the RxDatabase in the renderer processes. With the rxdb electron plugin you can create a remote RxStorage and consume it from the renderer process.
-
-To do this in a convenient way, the RxDB electron plugin provides the helper functions `exposeIpcMainRxStorage` and `getRxStorageIpcRenderer`.
-Similar to the [Worker RxStorage](./rx-storage-worker.md), these wrap any other [RxStorage](./rx-storage.md) once in the main process and once in each renderer process. In the renderer you can then use the storage to create a [RxDatabase](./rx-database.md) which communicates with the storage of the main process to store and query data.
-
-:::note
-`nodeIntegration` must be enabled in [Electron](https://www.electronjs.org/docs/latest/api/browser-window#new-browserwindowoptions).
-:::
-
-```ts
-// main.js
-const { exposeIpcMainRxStorage } = require('rxdb/plugins/electron');
-const { getRxStorageMemory } = require('rxdb/plugins/storage-memory');
-app.on('ready', async function () {
- exposeIpcMainRxStorage({
- key: 'main-storage',
- storage: getRxStorageMemory(),
- ipcMain: electron.ipcMain
- });
-});
-```
-
-```ts
-// renderer.js
-const { getRxStorageIpcRenderer } = require('rxdb/plugins/electron');
-const { getRxStorageMemory } = require('rxdb/plugins/storage-memory');
-
-const db = await createRxDatabase({
- name,
- storage: getRxStorageIpcRenderer({
- key: 'main-storage',
- ipcRenderer: electron.ipcRenderer
- })
-});
-/* ... */
-```
-
-## Related
-
-- [Comparison of Electron Databases](./electron-database.md)
diff --git a/docs/encryption.html b/docs/encryption.html
deleted file mode 100644
index 5f70792a5a3..00000000000
--- a/docs/encryption.html
+++ /dev/null
@@ -1,149 +0,0 @@
-
-
-
-
-
-Encryption | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The RxDB encryption plugin empowers developers to fortify their applications' data security. It seamlessly integrates with RxDB, allowing for the secure storage and retrieval of documents by encrypting them with a password. With encryption and decryption processes handled internally, it ensures that sensitive data remains confidential, making it a valuable tool for building robust, privacy-conscious applications. The encryption works on all RxDB supported devices types like the browser, ReactNative or Node.js.
-
-
Encrypting client-side stored data in RxDB offers numerous advantages:
-
-
Enhanced Security: In the unfortunate event of a user's device being stolen, the encrypted data remains safeguarded on the hard drive, inaccessible without the correct password.
-
Access Control: You can retain control over stored data by revoking access at any time simply by withholding the password.
-
Tamper proof Other applications on the device cannot read out the stored data when the password is only kept in the process-specific memory
RxDB handles the encryption and decryption of data internally. This means that when you work with a RxDocument, you can access the properties of the document just like you would with normal, unencrypted data. RxDB automatically decrypts the data for you when you retrieve it, making it transparent to your application code.
-This means the encryption works with all RxStorage like SQLite, IndexedDB, OPFS and so on.
-
However, there's a limitation when it comes to querying encrypted fields. Encrypted fields cannot be used as operators in queries. This means you cannot perform queries like "find all documents where the encrypted field equals a certain value." RxDB does not expose the encrypted data in a way that allows direct querying based on the encrypted content. To filter or search for documents based on the contents of encrypted fields, you would need to first decrypt the data and then perform the query, which might not be efficient or practical in some cases.
-You could however use the memory mapped RxStorage to replicate the encrypted documents into a non-encrypted in-memory storage and then query them like normal.
RxDB does not define how you should store or retrieve the encryption password. It only requires you to provide the password on database creation which grants you flexibility in how you manage encryption passwords.
-You could ask the user on app-start to insert the password, or you can retrieve the password from your backend on app start (or revoke access by no longer providing the password).
The encryption plugin itself uses symmetric encryption with a password to guarantee best performance when reading and storing data.
-It is not able to do Asymmetric encryption by itself. If you need Asymmetric encryption with a private/publicKey, it is recommended to encrypted the password itself with the asymmetric keys and store the encrypted password beside the other data. On app-start you can decrypt the password with the private key and use the decrypted password in the RxDB encryption plugin
The free encryption-crypto-js plugin that is based on the AES algorithm of the crypto-js library
-
The 👑 premiumencryption-web-crypto plugin that is based on the native Web Crypto API which makes it faster and more secure to use. Document inserts are about 10x faster compared to crypto-js and it has a smaller build size because it uses the browsers API instead of bundling an npm module.
-
-
An RxDB encryption plugin is a wrapper around any other RxStorage.
The password is set database specific and it is not possible to change the password of a database. Opening an existing database with a different password will throw an error. To change the password you can either:
Store a randomly created meta-password in a different RxDatabase as a value of a local document. Encrypt the meta password with the actual user password and read it out before creating the actual database.
If you are using Worker RxStorage or SharedWorker RxStorage with encryption, it's recommended to run encryption inside of the worker. Encryption can be very cpu intensive and would take away CPU-power from the main thread which is the main reason to use workers.
-
You do not need to worry about setting the password inside of the worker. The password will be set when calling createRxDatabase from the main thread, and will be passed internally to the storage in the worker automatically.
-
-
\ No newline at end of file
diff --git a/docs/encryption.md b/docs/encryption.md
deleted file mode 100644
index 0687b68ad55..00000000000
--- a/docs/encryption.md
+++ /dev/null
@@ -1,172 +0,0 @@
-# Encryption
-
-> Explore RxDB's 🔒 encryption plugin for enhanced data security in web and native apps, featuring password-based encryption and secure storage.
-
-import {Steps} from '@site/src/components/steps';
-
-# 🔒 Encrypted Local Storage with RxDB
-
-
-
-The RxDB encryption plugin empowers developers to fortify their applications' data security. It seamlessly integrates with [RxDB](https://rxdb.info/), allowing for the secure storage and retrieval of documents by **encrypting them with a password**. With encryption and decryption processes handled internally, it ensures that sensitive data remains confidential, making it a valuable tool for building robust, privacy-conscious applications. The encryption works on all RxDB supported devices types like the **browser**, **ReactNative** or **Node.js**.
-
-
-
-Encrypting client-side stored data in RxDB offers numerous advantages:
-- **Enhanced Security**: In the unfortunate event of a user's device being stolen, the encrypted data remains safeguarded on the hard drive, inaccessible without the correct password.
-- **Access Control**: You can retain control over stored data by revoking access at any time simply by withholding the password.
-- **Tamper proof** Other applications on the device cannot read out the stored data when the password is only kept in the process-specific memory
-
-## Querying encrypted data
-
-RxDB handles the encryption and decryption of data internally. This means that when you work with a RxDocument, you can access the properties of the document just like you would with normal, unencrypted data. RxDB automatically decrypts the data for you when you retrieve it, making it transparent to your application code.
-This means the encryption works with all [RxStorage](./rx-storage.md) like **SQLite**, **IndexedDB**, **OPFS** and so on.
-
-However, there's a limitation when it comes to querying encrypted fields. **Encrypted fields cannot be used as operators in queries**. This means you cannot perform queries like "find all documents where the encrypted field equals a certain value." RxDB does not expose the encrypted data in a way that allows direct querying based on the encrypted content. To filter or search for documents based on the contents of encrypted fields, you would need to first decrypt the data and then perform the query, which might not be efficient or practical in some cases.
-You could however use the [memory mapped](./rx-storage-memory-mapped.md) RxStorage to replicate the encrypted documents into a non-encrypted in-memory storage and then query them like normal.
-
-## Password handling
-RxDB does not define how you should store or retrieve the encryption password. It only requires you to provide the password on database creation which grants you flexibility in how you manage encryption passwords.
-You could ask the user on app-start to insert the password, or you can retrieve the password from your backend on app start (or revoke access by no longer providing the password).
-
-## Asymmetric encryption
-
-The encryption plugin itself uses **symmetric encryption** with a password to guarantee best performance when reading and storing data.
-It is not able to do **Asymmetric encryption** by itself. If you need Asymmetric encryption with a private/publicKey, it is recommended to encrypted the password itself with the asymmetric keys and store the encrypted password beside the other data. On app-start you can decrypt the password with the private key and use the decrypted password in the RxDB encryption plugin
-
-## Using the RxDB Encryption Plugins
-
-RxDB currently has two plugins for encryption:
-
-- The free `encryption-crypto-js` plugin that is based on the `AES` algorithm of the [crypto-js](https://www.npmjs.com/package/crypto-js) library
-- The [👑 premium](/premium/) `encryption-web-crypto` plugin that is based on the native [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) which makes it faster and more secure to use. Document inserts are about 10x faster compared to `crypto-js` and it has a smaller build size because it uses the browsers API instead of bundling an npm module.
-
-An RxDB encryption plugin is a wrapper around any other [RxStorage](./rx-storage.md).
-
-
-
-### Wrap your RxStorage with the encryption
-
-```ts
-import {
- wrappedKeyEncryptionCryptoJsStorage
-} from 'rxdb/plugins/encryption-crypto-js';
-import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
-
-// wrap the normal storage with the encryption plugin
-const encryptedStorage = wrappedKeyEncryptionCryptoJsStorage({
- storage: getRxStorageLocalstorage()
-});
-```
-
-### Create a RxDatabase with the wrapped storage
-
-Also you have to set a **password** when creating the database. The format of the password depends on which encryption plugin is used.
-
-```ts
-import { createRxDatabase } from 'rxdb/plugins/core';
-// create an encrypted database
-const db = await createRxDatabase({
- name: 'mydatabase',
- storage: encryptedStorage,
- password: 'sudoLetMeIn'
-});
-```
-
-### Create an RxCollection with an encrypted property
-
-To define a field as being encrypted, you have to add it to the `encrypted` fields list in the schema.
-
-```ts
-const schema = {
- version: 0,
- primaryKey: 'id',
- type: 'object',
- properties: {
- id: {
- type: 'string',
- maxLength: 100
- },
- secret: {
- type: 'string'
- },
- },
- required: ['id']
- encrypted: ['secret']
-};
-
-await db.addCollections({
- myDocuments: {
- schema
- }
-})
-```
-
-
-## Using Web-Crypto API
-
-For professionals, we have the `web-crypto` [👑 premium](/premium/) plugin which is faster and more secure:
-
-```ts
-import {
- wrappedKeyEncryptionWebCryptoStorage,
- createPassword
-} from 'rxdb-premium/plugins/encryption-web-crypto';
-import { getRxStorageIndexedDB } from 'rxdb-premium/plugins/storage-indexeddb';
-
-// wrap the normal storage with the encryption plugin
-const encryptedIndexedDbStorage = wrappedKeyEncryptionWebCryptoStorage({
- storage: getRxStorageIndexedDB()
-});
-
-const myPasswordObject = {
- // Algorithm can be oneOf: 'AES-CTR' | 'AES-CBC' | 'AES-GCM'
- algorithm: 'AES-CTR',
- password: 'myRandomPasswordWithMin8Length'
-};
-
-// create an encrypted database
-const db = await createRxDatabase({
- name: 'mydatabase',
- storage: encryptedIndexedDbStorage,
- password: myPasswordObject
-});
-
-/* ... */
-```
-
-## Changing the password
-
-The password is set database specific and it is not possible to change the password of a database. Opening an existing database with a different password will throw an error. To change the password you can either:
-- Use the [storage migration plugin](./migration-storage.md) to migrate the database state into a new database.
-- Store a randomly created meta-password in a different RxDatabase as a value of a [local document](./rx-local-document.md). Encrypt the meta password with the actual user password and read it out before creating the actual database.
-
-## Encrypted attachments
-
-To store the [attachments](./rx-attachment.md) data encrypted, you have to set `encrypted: true` in the `attachments` property of the schema.
-
-```ts
-const mySchema = {
- version: 0,
- type: 'object',
- properties: {
- /* ... */
- },
- attachments: {
- encrypted: true // if true, the attachment-data will be encrypted with the db-password
- }
-};
-```
-
-## Encryption and workers
-
-If you are using [Worker RxStorage](./rx-storage-worker.md) or [SharedWorker RxStorage](./rx-storage-shared-worker.md) with encryption, it's recommended to run encryption inside of the worker. Encryption can be very cpu intensive and would take away CPU-power from the main thread which is the main reason to use workers.
-
-You do not need to worry about setting the password inside of the worker. The password will be set when calling createRxDatabase from the main thread, and will be passed internally to the storage in the worker automatically.
diff --git a/docs/errors.html b/docs/errors.html
deleted file mode 100644
index 3d8a9c5894c..00000000000
--- a/docs/errors.html
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
-
-Error Messages | RxDB - JavaScript Database
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
When RxDB has an error, an RxError object is thrown instead of a normal JavaScript Error. This RxError contains additional properties such as a code field and parameters. By default the full human readable error messages are not included into the RxDB build. This is because error messages have a high entropy and cannot be compressed well. Therefore only an error message with the correct error-code and parameters is thrown but without the full text.
-When you enable the DevMode Plugin the full error messages are added to the RxError. This should only be done in development, not in production builds to keep a small build size.
Collection- and database-names must match the regex to be compatible with couchdb databases.
- See https://neighbourhood.ie/blog/2020/10/13/everything-you-need-to-know-about-couchdb-database-names/
- info: if your database-name specifies a folder, the name must contain the slash-char '/' or '\'
Crypto.subtle.digest is not available in your runtime. For expo/react-native see https://discord.com/channels/969553741705539624/1341392686267109458/1343639513850843217
Running a count() query in slow mode is now allowed. Either run a count() query with a selector that fully matches an index or set allowSlowCount=true when calling the createRxDatabase
$regex queries must be defined by a string, not an RegExp instance. This is because RegExp objects cannot be JSON stringified and also they are mutable which would be dangerous
Malformed query result data. This likely happens because you create a OPFS-storage RxDatabase inside of a worker but did not set the usesRxDatabaseInWorker setting. https://rxdb.info/rx-storage-opfs.html?console=opfs#setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker
RxDatabase.addCollections(): another instance created this collection with a different schema. Read thishttps://rxdb.info/rx-schema.html?console=qa#faq
CreateRxDatabase(): A RxDatabase with the same name and adapter already exists.
-Make sure to use this combination of storage+databaseName only once
-If you have the duplicate database on purpose to simulate multi-tab behavior in unit tests, set "ignoreDuplicate: true".
-As alternative you can set "closeDuplicates: true" like if this happens in your react projects with hot reload that reloads the code without reloading the process.
In the open-source version of RxDB, the amount of collections that can exist in parallel is limited to 16. If you already purchased the premium access, you can remove this limit: https://rxdb.info/rx-collection.html?console=limit#faq
Given document data could not be structured cloned. This happens if you pass non-plain-json data into it, like a Date() object or a Function. In vue.js this happens if you use ref() on the document data which transforms it into a Proxy object.
Cannot open database state with newer RxDB version. You have to migrate your database state first. See https://rxdb.info/migration-storage.html?console=storage
RxCouchDBReplicationState.awaitInitialReplication() cannot await initial replication if multiInstance because the replication might run on another instance
$ref fields in the schema are not allowed. RxDB cannot resolve related schemas because it would have a negative performance impact.It would have to run http requests on runtime. $ref fields should be resolved during build time.
Primary key and also indexed fields which are strings, must have a maxLength that is <= 2048. Notice that having a big maxLength can negatively affect the performance. Only set it as big as it has to be.
When dev-mode is enabled, your storage must use one of the schema validators at the top level. This is because most problems people have with RxDB is because they store data that is not valid to the schema which causes strange bugs and problems.